From 633561ada27a3bef816c628a8049e31416a431cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Trevisan=20=28Trevi=C3=B1o=29?= Date: Wed, 14 Sep 2022 04:16:56 +0200 Subject: [PATCH] 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 82a34683c..9fbce402c 100644 --- a/docs/reference/glib/glib-sections.txt.in +++ b/docs/reference/glib/glib-sections.txt.in @@ -411,6 +411,8 @@ G_GNUC_INTERNAL G_GNUC_MAY_ALIAS +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 041d90ce3..1d24a8487 100644 --- a/glib/docs.c +++ b/glib/docs.c @@ -2527,6 +2527,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. + * + * |[ + * #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: * @@ -2537,7 +2578,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 */ @@ -2560,6 +2601,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 7355a0d0c..f0db84938 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 aebf1da56..86bf21b57 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 38372518b..89d74d3a5 100644 --- a/glib/tests/macros.c +++ b/glib/tests/macros.c @@ -27,6 +27,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 @@ -71,6 +146,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 6bea5d24b..a01649b77 100644 --- a/glib/tests/meson.build +++ b/glib/tests/meson.build @@ -306,7 +306,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