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.
This commit is contained in:
Marco Trevisan (Treviño) 2022-09-14 04:16:56 +02:00
parent 14ba699508
commit 633561ada2
6 changed files with 152 additions and 2 deletions

View File

@ -411,6 +411,8 @@ G_GNUC_INTERNAL
G_GNUC_MAY_ALIAS G_GNUC_MAY_ALIAS
<SUBSECTION> <SUBSECTION>
G_C_STD_VERSION
G_C_STD_CHECK_VERSION
G_CXX_STD_VERSION G_CXX_STD_VERSION
G_CXX_STD_CHECK_VERSION G_CXX_STD_CHECK_VERSION

View File

@ -2527,6 +2527,47 @@
* Since: 2.6 * 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: * G_CXX_STD_VERSION:
* *
@ -2537,7 +2578,7 @@
* *
* This is granted to be undefined when not compiling with a C++ compiler. * 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 * Since: 2.76
*/ */
@ -2560,6 +2601,8 @@
* #endif * #endif
* ]| * ]|
* *
* See also: %G_C_STD_CHECK_VERSION
*
* Returns: %TRUE if @version is supported by the compiler, %FALSE otherwise * Returns: %TRUE if @version is supported by the compiler, %FALSE otherwise
* *
* Since: 2.76 * Since: 2.76

View File

@ -69,8 +69,26 @@
# undef G_CXX_STD_VERSION # undef G_CXX_STD_VERSION
# define G_CXX_STD_CHECK_VERSION(version) (0) # 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) */ #else /* defined (__cplusplus) */
# undef G_C_STD_VERSION
# define G_C_STD_CHECK_VERSION(version) (0)
# if defined (_MSVC_LANG) # if defined (_MSVC_LANG)
# define G_CXX_STD_VERSION (_MSVC_LANG > __cplusplus ? _MSVC_LANG : __cplusplus) # define G_CXX_STD_VERSION (_MSVC_LANG > __cplusplus ? _MSVC_LANG : __cplusplus)
# else # else

View File

@ -23,7 +23,12 @@
#error G_CXX_STD_VERSION is not defined #error G_CXX_STD_VERSION is not defined
#endif #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_CXX_STD_VERSION);
G_STATIC_ASSERT (!G_C_STD_CHECK_VERSION (99));
#if G_CXX_STD_VERSION >= 199711L #if G_CXX_STD_VERSION >= 199711L
G_STATIC_ASSERT (G_CXX_STD_CHECK_VERSION (98)); G_STATIC_ASSERT (G_CXX_STD_CHECK_VERSION (98));

View File

@ -27,6 +27,81 @@
#endif #endif
G_STATIC_ASSERT (!G_CXX_STD_CHECK_VERSION (98)); 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 */ /* Test that G_STATIC_ASSERT_EXPR can be used as an expression */
static void static void
@ -71,6 +146,10 @@ main (int argc,
{ {
g_test_init (&argc, &argv, NULL); 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 ("/alignof/fallback", test_alignof_fallback);
g_test_add_func ("/assert/static", test_assert_static); g_test_add_func ("/assert/static", test_assert_static);
g_test_add_func ("/struct/sizeof_member", test_struct_sizeof_member); g_test_add_func ("/struct/sizeof_member", test_struct_sizeof_member);

View File

@ -306,7 +306,10 @@ foreach test_name, extra_args : glib_tests
'@0@-c-@1@'.format(test_name, std) : extra_args + { '@0@-c-@1@'.format(test_name, std) : extra_args + {
'source' : extra_args.get('source', test_name + '.c'), 'source' : extra_args.get('source', test_name + '.c'),
'suite' : ['cc'] + extra_args.get('suite', []), '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 endif