diff --git a/docs/reference/glib/glib-sections.txt.in b/docs/reference/glib/glib-sections.txt.in index face9dec6..82a34683c 100644 --- a/docs/reference/glib/glib-sections.txt.in +++ b/docs/reference/glib/glib-sections.txt.in @@ -410,6 +410,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 1ca7927fb..041d90ce3 100644 --- a/glib/docs.c +++ b/glib/docs.c @@ -2527,6 +2527,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 36d2238fc..c892c0b44 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 aa5e0cb57..a2f8e4e3f 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; @@ -186,12 +295,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, NULL); #else g_test_init (&argc, &argv, static_cast(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 efe632b52..38372518b 100644 --- a/glib/tests/macros.c +++ b/glib/tests/macros.c @@ -22,6 +22,12 @@ #include +#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 2af5871ff..3ec63508b 100644 --- a/glib/tests/meson.build +++ b/glib/tests/meson.build @@ -187,7 +187,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