diff --git a/docs/reference/glib/glib-sections.txt.in b/docs/reference/glib/glib-sections.txt.in index 90b0f3258..144e7077b 100644 --- a/docs/reference/glib/glib-sections.txt.in +++ b/docs/reference/glib/glib-sections.txt.in @@ -411,6 +411,10 @@ G_HAVE_GNUC_VISIBILITY G_GNUC_INTERNAL G_GNUC_MAY_ALIAS + +G_CXX_STD_VERSION +G_CXX_STD_CHECK_VERSION + G_DEPRECATED G_DEPRECATED_FOR diff --git a/glib/docs.c b/glib/docs.c index f93ce8048..a96e067fb 100644 --- a/glib/docs.c +++ b/glib/docs.c @@ -2167,6 +2167,44 @@ * Since: 2.6 */ +/** + * G_CXX_STD_VERSION: + * + * The C++ standard version the code is compiling against, it's defined + * with the same value of `__cplusplus` for C++ standard compatible + * compilers, while it uses `_MSVC_LANG` in MSVC, given that the + * standard definition depends on a compilation flag in such compiler. + * + * This is granted to be undefined when not compiling with a C++ compiler. + * + * See also: %G_CXX_STD_CHECK_VERSION + * + * Since: 2.76 + */ + +/** + * G_CXX_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. `11`, `17`...) or in + * the complete form otherwise (e.g. `201103L`, `201703L`, `205503L`...). + * + * When a C compiler is used, the macro is defined and returns always %FALSE. + * + * This value is compared against %G_CXX_STD_VERSION. + * + * |[ + * #if G_CXX_STD_CHECK_VERSION(20) + * #endif + * ]| + * + * Returns: %TRUE if @version is supported by the compiler, %FALSE otherwise + * + * Since: 2.76 + */ + /** * G_LIKELY: * @expr: the expression diff --git a/glib/gmacros.h b/glib/gmacros.h index f9e20407e..a915a8b7a 100644 --- a/glib/gmacros.h +++ b/glib/gmacros.h @@ -64,6 +64,31 @@ #define G_GNUC_EXTENSION #endif +#if !defined (__cplusplus) + +# undef G_CXX_STD_VERSION +# define G_CXX_STD_CHECK_VERSION(version) (0) + +#else /* defined (__cplusplus) */ + +# if defined (_MSVC_LANG) +# define G_CXX_STD_VERSION (_MSVC_LANG > __cplusplus ? _MSVC_LANG : __cplusplus) +# else +# define G_CXX_STD_VERSION __cplusplus +# endif /* defined(_MSVC_LANG) */ + +# define G_CXX_STD_CHECK_VERSION(version) ( \ + ((version) >= 199711L && (version) <= G_CXX_STD_VERSION) || \ + ((version) == 98 && G_CXX_STD_VERSION >= 199711L) || \ + ((version) == 03 && G_CXX_STD_VERSION >= 199711L) || \ + ((version) == 11 && G_CXX_STD_VERSION >= 201103L) || \ + ((version) == 14 && G_CXX_STD_VERSION >= 201402L) || \ + ((version) == 17 && G_CXX_STD_VERSION >= 201703L) || \ + ((version) == 20 && G_CXX_STD_VERSION >= 202002L) || \ + 0) + +#endif /* !defined (__cplusplus) */ + /* Every compiler that we target supports inlining, but some of them may * complain about it if we don't say "__inline". If we have C99, or if * we are using C++, then we can use "inline" directly. Unfortunately diff --git a/glib/tests/cxx.cpp b/glib/tests/cxx.cpp index 6ac60791c..e99f6f21c 100644 --- a/glib/tests/cxx.cpp +++ b/glib/tests/cxx.cpp @@ -19,6 +19,115 @@ #include +#if !defined (G_CXX_STD_VERSION) || !defined (G_CXX_STD_CHECK_VERSION) +#error G_CXX_STD_VERSION is not defined +#endif + +G_STATIC_ASSERT (G_CXX_STD_VERSION); + +#if G_CXX_STD_VERSION >= 199711L + G_STATIC_ASSERT (G_CXX_STD_CHECK_VERSION (98)); + G_STATIC_ASSERT (G_CXX_STD_CHECK_VERSION (199711L)); + G_STATIC_ASSERT (G_CXX_STD_CHECK_VERSION (03)); +#endif + +#if G_CXX_STD_VERSION == 199711L + G_STATIC_ASSERT (!G_CXX_STD_CHECK_VERSION (11)); + G_STATIC_ASSERT (!G_CXX_STD_CHECK_VERSION (201103L)); + G_STATIC_ASSERT (!G_CXX_STD_CHECK_VERSION (14)); + G_STATIC_ASSERT (!G_CXX_STD_CHECK_VERSION (201402L)); + G_STATIC_ASSERT (!G_CXX_STD_CHECK_VERSION (17)); + G_STATIC_ASSERT (!G_CXX_STD_CHECK_VERSION (201703L)); + G_STATIC_ASSERT (!G_CXX_STD_CHECK_VERSION (20)); + G_STATIC_ASSERT (!G_CXX_STD_CHECK_VERSION (202002L)); +#endif + +#if G_CXX_STD_VERSION >= 201103L + G_STATIC_ASSERT (G_CXX_STD_CHECK_VERSION (98)); + G_STATIC_ASSERT (G_CXX_STD_CHECK_VERSION (199711L)); + G_STATIC_ASSERT (G_CXX_STD_CHECK_VERSION (03)); + G_STATIC_ASSERT (G_CXX_STD_CHECK_VERSION (11)); + G_STATIC_ASSERT (G_CXX_STD_CHECK_VERSION (201103L)); +#endif + +#if G_CXX_STD_VERSION == 201103L + G_STATIC_ASSERT (!G_CXX_STD_CHECK_VERSION (14)); + G_STATIC_ASSERT (!G_CXX_STD_CHECK_VERSION (201402L)); + G_STATIC_ASSERT (!G_CXX_STD_CHECK_VERSION (17)); + G_STATIC_ASSERT (!G_CXX_STD_CHECK_VERSION (201703L)); + G_STATIC_ASSERT (!G_CXX_STD_CHECK_VERSION (20)); + G_STATIC_ASSERT (!G_CXX_STD_CHECK_VERSION (202002L)); +#endif + +#if G_CXX_STD_VERSION >= 201402L + G_STATIC_ASSERT (G_CXX_STD_CHECK_VERSION (14)); + G_STATIC_ASSERT (G_CXX_STD_CHECK_VERSION (201402L)); +#endif + +#if G_CXX_STD_VERSION == 201402L + G_STATIC_ASSERT (!G_CXX_STD_CHECK_VERSION (17)); + G_STATIC_ASSERT (!G_CXX_STD_CHECK_VERSION (201703L)); + G_STATIC_ASSERT (!G_CXX_STD_CHECK_VERSION (20)); + G_STATIC_ASSERT (!G_CXX_STD_CHECK_VERSION (202002L)); +#endif + +#if G_CXX_STD_VERSION >= 201703L + G_STATIC_ASSERT (G_CXX_STD_CHECK_VERSION (17)); + G_STATIC_ASSERT (G_CXX_STD_CHECK_VERSION (201703L)); +#endif + +#if G_CXX_STD_VERSION == 201703L + G_STATIC_ASSERT (!G_CXX_STD_CHECK_VERSION (20)); + G_STATIC_ASSERT (!G_CXX_STD_CHECK_VERSION (202002L)); +#endif + +#if G_CXX_STD_VERSION >= 202002L + G_STATIC_ASSERT (G_CXX_STD_CHECK_VERSION (20)); + G_STATIC_ASSERT (G_CXX_STD_CHECK_VERSION (202002L)); +#endif + +#if G_CXX_STD_VERSION == 202002L + G_STATIC_ASSERT (!G_CXX_STD_CHECK_VERSION (23)); + G_STATIC_ASSERT (!G_CXX_STD_CHECK_VERSION (202300L)); +#endif + +#ifdef _G_EXPECTED_CXX_STANDARD +static void +test_cpp_standard (void) +{ + guint64 std_version = 0; + + if (!g_ascii_string_to_unsigned (_G_EXPECTED_CXX_STANDARD, 10, 0, G_MAXUINT64, + &std_version, NULL)) + { + g_test_skip ("Expected standard value is non-numeric: " + _G_EXPECTED_CXX_STANDARD); + return; + } + +#if !G_GNUC_CHECK_VERSION (11, 0) + if (std_version >= 20) + { + // See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=93821 + g_test_skip ("Expected standard version is not properly supported by compiler"); + return; + } +#endif + + g_test_message ("Checking if '" G_STRINGIFY (G_CXX_STD_VERSION) "' respects " + "the expected standard version '" _G_EXPECTED_CXX_STANDARD "'"); + g_assert_true (G_CXX_STD_CHECK_VERSION (std_version)); + + if (std_version < 10 || std_version > 90) + std_version = 97; + + if (std_version >= 90) + g_assert_cmpuint (G_CXX_STD_VERSION, >=, (std_version + 1900) * 100); + else + g_assert_cmpuint (G_CXX_STD_VERSION, >=, (std_version + 2000) * 100); +} +#endif + typedef struct { int dummy; @@ -34,7 +143,7 @@ test_typeof (void) MyObject *obj3 = g_atomic_pointer_get (&obj2); g_assert_true (obj3 == obj); -#if __cplusplus >= 201103L +#if G_CXX_STD_CHECK_VERSION (11) MyObject *obj4 = nullptr; #else MyObject *obj4 = NULL; @@ -42,7 +151,7 @@ test_typeof (void) g_atomic_pointer_set (&obj4, obj3); g_assert_true (obj4 == obj); -#if __cplusplus >= 201103L +#if G_CXX_STD_CHECK_VERSION (11) MyObject *obj5 = nullptr; g_atomic_pointer_compare_and_exchange (&obj5, nullptr, obj4); #else @@ -195,12 +304,15 @@ test_steal_pointer (void) int main (int argc, char *argv[]) { -#if __cplusplus >= 201103L +#if G_CXX_STD_CHECK_VERSION (11) g_test_init (&argc, &argv, nullptr); #else g_test_init (&argc, &argv, NULL); #endif +#ifdef _G_EXPECTED_CXX_STANDARD + g_test_add_func ("/C++/check-standard-" _G_EXPECTED_CXX_STANDARD, test_cpp_standard); +#endif g_test_add_func ("/C++/typeof", test_typeof); g_test_add_func ("/C++/atomic-pointer-compare-and-exchange", test_atomic_pointer_compare_and_exchange); g_test_add_func ("/C++/atomic-pointer-compare-and-exchange-full", test_atomic_pointer_compare_and_exchange_full); diff --git a/glib/tests/macros.c b/glib/tests/macros.c index cbbc0f6de..b18f3a3a6 100644 --- a/glib/tests/macros.c +++ b/glib/tests/macros.c @@ -33,6 +33,12 @@ # endif #endif +#ifdef G_CXX_STD_VERSION +#error G_CXX_STD_VERSION should be undefined in C programs +#endif + +G_STATIC_ASSERT (!G_CXX_STD_CHECK_VERSION (98)); + /* Test that G_STATIC_ASSERT_EXPR can be used as an expression */ static void test_assert_static (void) diff --git a/glib/tests/meson.build b/glib/tests/meson.build index 8b5c58b8c..429218cf5 100644 --- a/glib/tests/meson.build +++ b/glib/tests/meson.build @@ -168,7 +168,7 @@ if have_cxx 'cxx-@0@'.format(std) : { 'source' : ['cxx.cpp'], 'suite' : ['cpp'], - 'cpp_args' : [arg], + 'cpp_args' : [arg, '-D_G_EXPECTED_CXX_STANDARD="@0@"'.format(std)], }, } endforeach