[glib/c-cxx-std-versions: 6/7] macros: Add a generic way to get and check the supported C standard




commit 6ce4984fdd6bc24dc5149cb3f6bec36d4f6b3cf1
Author: Marco Trevisan (TreviƱo) <mail 3v1n0 net>
Date:   Wed Sep 14 04:16:56 2022 +0200

    macros: Add a generic way to get and check the supported C standard
    
    Try to get the value of __STDC_VERSION__ if supported, if not just
    fallback to the oldest standard that any compiler should handle.

 docs/reference/glib/glib-sections.txt.in |  2 +
 glib/docs.c                              | 45 +++++++++++++++++-
 glib/gmacros.h                           | 18 ++++++++
 glib/tests/cxx.cpp                       |  5 ++
 glib/tests/macros.c                      | 79 ++++++++++++++++++++++++++++++++
 glib/tests/meson.build                   |  5 +-
 6 files changed, 152 insertions(+), 2 deletions(-)
---
diff --git a/docs/reference/glib/glib-sections.txt.in b/docs/reference/glib/glib-sections.txt.in
index 144e7077b0..96a432b5f4 100644
--- a/docs/reference/glib/glib-sections.txt.in
+++ b/docs/reference/glib/glib-sections.txt.in
@@ -412,6 +412,8 @@ G_GNUC_INTERNAL
 G_GNUC_MAY_ALIAS
 
 <SUBSECTION>
+G_C_STD_VERSION
+G_C_STD_CHECK_VERSION
 G_CXX_STD_VERSION
 G_CXX_STD_CHECK_VERSION
 
diff --git a/glib/docs.c b/glib/docs.c
index a96e067fbe..2453016289 100644
--- a/glib/docs.c
+++ b/glib/docs.c
@@ -2167,6 +2167,47 @@
  * Since: 2.6
  */
 
+/**
+ * G_C_STD_VERSION:
+ *
+ * The C standard version the code is compiling against, it's normally
+ * defined with the same value of `__STDC_VERSION__` for C standard
+ * compatible compilers, while it uses the lowest standard version
+ * in pure MSVC, given that in such compiler the definition depends on
+ * a compilation flag.
+ *
+ * This is granted to be undefined when compiling with a C++ compiler.
+ *
+ * See also: %G_C_STD_CHECK_VERSION and %G_CXX_STD_VERSION
+ *
+ * Since: 2.76
+ */
+
+/**
+ * G_C_STD_CHECK_VERSION:
+ * @version: The C version to be checked for compatibility
+ *
+ * Macro to check if the current compiler supports a specified @version
+ * of the C standard. Such value must be numeric and can be provided both
+ * in the short form for the well-known versions (e.g. `90`, `99`...) or in
+ * the complete form otherwise (e.g. `199000L`, `199901L`, `205503L`...).
+ *
+ * When a C++ compiler is used, the macro is defined and returns always %FALSE.
+ *
+ * This value is compared against %G_C_STD_VERSION.
+ *
+ * |[<!-- language="C" -->
+ * #if G_C_STD_CHECK_VERSION(17)
+ * #endif
+ * ]|
+ *
+ * See also: %G_CXX_STD_CHECK_VERSION
+ *
+ * Returns: %TRUE if @version is supported by the compiler, %FALSE otherwise
+ *
+ * Since: 2.76
+ */
+
 /**
  * G_CXX_STD_VERSION:
  *
@@ -2177,7 +2218,7 @@
  *
  * This is granted to be undefined when not compiling with a C++ compiler.
  *
- * See also: %G_CXX_STD_CHECK_VERSION
+ * See also: %G_CXX_STD_CHECK_VERSION and %G_C_STD_VERSION
  *
  * Since: 2.76
  */
@@ -2200,6 +2241,8 @@
  * #endif
  * ]|
  *
+ * See also: %G_C_STD_CHECK_VERSION
+ *
  * Returns: %TRUE if @version is supported by the compiler, %FALSE otherwise
  *
  * Since: 2.76
diff --git a/glib/gmacros.h b/glib/gmacros.h
index 4c9ffbc4c6..a110f42db2 100644
--- a/glib/gmacros.h
+++ b/glib/gmacros.h
@@ -69,8 +69,26 @@
 # undef G_CXX_STD_VERSION
 # define G_CXX_STD_CHECK_VERSION(version) (0)
 
+# if defined (__STDC_VERSION__)
+#  define G_C_STD_VERSION __STDC_VERSION__
+# else
+#  define G_C_STD_VERSION 199000L
+# endif /* defined (__STDC_VERSION__) */
+
+# define G_C_STD_CHECK_VERSION(version) ( \
+  ((version) >= 199000L && (version) <= G_C_STD_VERSION) || \
+  ((version) == 89 && G_C_STD_VERSION >= 199000L) || \
+  ((version) == 90 && G_C_STD_VERSION >= 199000L) || \
+  ((version) == 99 && G_C_STD_VERSION >= 199901L) || \
+  ((version) == 11 && G_C_STD_VERSION >= 201112L) || \
+  ((version) == 17 && G_C_STD_VERSION >= 201710L) || \
+  0)
+
 #else /* defined (__cplusplus) */
 
+# undef G_C_STD_VERSION
+# define G_C_STD_CHECK_VERSION(version) (0)
+
 # if defined (_MSVC_LANG)
 #  define G_CXX_STD_VERSION (_MSVC_LANG > __cplusplus ? _MSVC_LANG : __cplusplus)
 # else
diff --git a/glib/tests/cxx.cpp b/glib/tests/cxx.cpp
index 5d9d537a5a..ce354d1a6b 100644
--- a/glib/tests/cxx.cpp
+++ b/glib/tests/cxx.cpp
@@ -23,7 +23,12 @@
 #error G_CXX_STD_VERSION is not defined
 #endif
 
+#ifdef G_C_STD_VERSION
+#error G_C_STD_VERSION should be undefined in C programs
+#endif
+
 G_STATIC_ASSERT (G_CXX_STD_VERSION);
+G_STATIC_ASSERT (!G_C_STD_CHECK_VERSION (99));
 
 #if G_CXX_STD_VERSION >= 199711L
   G_STATIC_ASSERT (G_CXX_STD_CHECK_VERSION (98));
diff --git a/glib/tests/macros.c b/glib/tests/macros.c
index b18f3a3a6d..8eb6fbca1c 100644
--- a/glib/tests/macros.c
+++ b/glib/tests/macros.c
@@ -38,6 +38,81 @@
 #endif
 
 G_STATIC_ASSERT (!G_CXX_STD_CHECK_VERSION (98));
+G_STATIC_ASSERT (G_C_STD_CHECK_VERSION (89));
+G_STATIC_ASSERT (G_C_STD_CHECK_VERSION (90));
+
+#if G_C_STD_VERSION >= 199000L
+  G_STATIC_ASSERT (G_C_STD_CHECK_VERSION (89));
+  G_STATIC_ASSERT (G_C_STD_CHECK_VERSION (90));
+  G_STATIC_ASSERT (G_C_STD_CHECK_VERSION (199000L));
+#endif
+
+#if G_C_STD_VERSION == 198900L
+  G_STATIC_ASSERT (!G_C_STD_CHECK_VERSION (99));
+  G_STATIC_ASSERT (!G_C_STD_CHECK_VERSION (199901L));
+  G_STATIC_ASSERT (!G_C_STD_CHECK_VERSION (11));
+  G_STATIC_ASSERT (!G_C_STD_CHECK_VERSION (201112L));
+  G_STATIC_ASSERT (!G_C_STD_CHECK_VERSION (17));
+  G_STATIC_ASSERT (!G_C_STD_CHECK_VERSION (201710L));
+#endif
+
+#if G_C_STD_VERSION >= 199901L
+  G_STATIC_ASSERT (G_C_STD_CHECK_VERSION (99));
+  G_STATIC_ASSERT (G_C_STD_CHECK_VERSION (199901L));
+#endif
+
+#if G_C_STD_VERSION == 199901L
+  G_STATIC_ASSERT (!G_C_STD_CHECK_VERSION (11));
+  G_STATIC_ASSERT (!G_C_STD_CHECK_VERSION (201112L));
+  G_STATIC_ASSERT (!G_C_STD_CHECK_VERSION (17));
+  G_STATIC_ASSERT (!G_C_STD_CHECK_VERSION (201710L));
+#endif
+
+#if G_C_STD_VERSION >= 201112L
+  G_STATIC_ASSERT (G_C_STD_CHECK_VERSION (11));
+  G_STATIC_ASSERT (G_C_STD_CHECK_VERSION (201112L));
+#endif
+
+#if G_C_STD_VERSION == 201112L
+  G_STATIC_ASSERT (!G_C_STD_CHECK_VERSION (17));
+  G_STATIC_ASSERT (!G_C_STD_CHECK_VERSION (201710L));
+#endif
+
+#if G_C_STD_VERSION >= 201710L
+  G_STATIC_ASSERT (G_C_STD_CHECK_VERSION (17));
+  G_STATIC_ASSERT (G_C_STD_CHECK_VERSION (201710L));
+#endif
+
+#if G_C_STD_VERSION == 201710L
+  G_STATIC_ASSERT (!G_C_STD_CHECK_VERSION (23));
+  G_STATIC_ASSERT (!G_C_STD_CHECK_VERSION (202300L));
+#endif
+
+#ifdef _G_EXPECTED_C_STANDARD
+static void
+test_c_standard (void)
+{
+  guint64 std_version = 0;
+
+  if (!g_ascii_string_to_unsigned (_G_EXPECTED_C_STANDARD, 10, 0, G_MAXUINT64,
+                                   &std_version, NULL))
+    {
+      g_test_skip ("Expected standard value is non-numeric: "
+                   _G_EXPECTED_C_STANDARD);
+      return;
+    }
+
+  g_assert_true (G_C_STD_CHECK_VERSION (std_version));
+
+  if (std_version > 80 && std_version < 99)
+    std_version = 90;
+
+  if (std_version >= 90)
+    g_assert_cmpuint (G_C_STD_VERSION, >=, (std_version + 1900) * 100);
+  else
+    g_assert_cmpuint (G_C_STD_VERSION, >=, (std_version + 2000) * 100);
+}
+#endif
 
 /* Test that G_STATIC_ASSERT_EXPR can be used as an expression */
 static void
@@ -82,6 +157,10 @@ main (int   argc,
 {
   g_test_init (&argc, &argv, NULL);
 
+#ifdef _G_EXPECTED_C_STANDARD
+  g_test_add_func ("/C/standard-" _G_EXPECTED_C_STANDARD, test_c_standard);
+#endif
+
   g_test_add_func ("/alignof/fallback", test_alignof_fallback);
   g_test_add_func ("/assert/static", test_assert_static);
   g_test_add_func ("/struct/sizeof_member", test_struct_sizeof_member);
diff --git a/glib/tests/meson.build b/glib/tests/meson.build
index 5108944c19..f9f47cf917 100644
--- a/glib/tests/meson.build
+++ b/glib/tests/meson.build
@@ -270,7 +270,10 @@ foreach test_name, extra_args : glib_tests
         '@0@-c-@1@'.format(test_name, std) : extra_args + {
           'source' : extra_args.get('source', test_name + '.c'),
           'suite' : ['cc'] + extra_args.get('suite', []),
-          'c_args' : [c_standards.get(std)] + extra_args.get('c_args', []),
+          'c_args' : [
+            c_standards.get(std),
+            '-D_G_EXPECTED_C_STANDARD="@0@"'.format(std)
+          ] + extra_args.get('c_args', []),
         }
       }
     endif


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]