diff --git a/docs/reference/glib/glib-sections.txt.in b/docs/reference/glib/glib-sections.txt.in index face9dec6..9fbce402c 100644 --- a/docs/reference/glib/glib-sections.txt.in +++ b/docs/reference/glib/glib-sections.txt.in @@ -410,6 +410,12 @@ G_HAVE_GNUC_VISIBILITY 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 + G_DEPRECATED G_DEPRECATED_FOR diff --git a/glib/docs.c b/glib/docs.c index 1ca7927fb..1d24a8487 100644 --- a/glib/docs.c +++ b/glib/docs.c @@ -2527,6 +2527,87 @@ * 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: + * + * 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 and %G_C_STD_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 + * ]| + * + * See also: %G_C_STD_CHECK_VERSION + * + * Returns: %TRUE if @version is supported by the compiler, %FALSE otherwise + * + * Since: 2.76 + */ + /** * G_LIKELY: * @expr: the expression diff --git a/glib/gatomic.h b/glib/gatomic.h index 8c6a0877e..148424dc3 100644 --- a/glib/gatomic.h +++ b/glib/gatomic.h @@ -170,7 +170,7 @@ G_END_DECLS (void) (0 ? *(atomic) ^ *(atomic) : 1); \ __atomic_fetch_sub ((atomic), 1, __ATOMIC_SEQ_CST) == 1; \ })) -#if defined(glib_typeof) && defined(__cplusplus) +#if defined(glib_typeof) && defined(G_CXX_STD_VERSION) /* See comments below about equivalent g_atomic_pointer_compare_and_exchange() * shenanigans for type-safety when compiling in C++ mode. */ #define g_atomic_int_compare_and_exchange(atomic, oldval, newval) \ @@ -180,7 +180,7 @@ G_END_DECLS (void) (0 ? *(atomic) ^ (newval) ^ (oldval) : 1); \ __atomic_compare_exchange_n ((atomic), &gaicae_oldval, (newval), FALSE, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) ? TRUE : FALSE; \ })) -#else /* if !(defined(glib_typeof) && defined(__cplusplus)) */ +#else /* if !(defined(glib_typeof) && defined(G_CXX_STD_VERSION)) */ #define g_atomic_int_compare_and_exchange(atomic, oldval, newval) \ (G_GNUC_EXTENSION ({ \ gint gaicae_oldval = (oldval); \ @@ -230,7 +230,7 @@ G_END_DECLS (guint) __atomic_fetch_xor ((atomic), (val), __ATOMIC_SEQ_CST); \ })) -#if defined(glib_typeof) && defined(__cplusplus) +#if defined(glib_typeof) && defined(G_CXX_STD_VERSION) /* This is typesafe because we check we can assign oldval to the type of * (*atomic). Unfortunately it can only be done in C++ because gcc/clang warn * when atomic is volatile and not oldval, or when atomic is gsize* and oldval @@ -248,7 +248,7 @@ G_END_DECLS (void) (0 ? (gpointer) *(atomic) : NULL); \ __atomic_compare_exchange_n ((atomic), &gapcae_oldval, (newval), FALSE, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) ? TRUE : FALSE; \ })) -#else /* if !(defined(glib_typeof) && defined(__cplusplus) */ +#else /* if !(defined(glib_typeof) && defined(G_CXX_STD_VERSION) */ #define g_atomic_pointer_compare_and_exchange(atomic, oldval, newval) \ (G_GNUC_EXTENSION ({ \ G_STATIC_ASSERT (sizeof (oldval) == sizeof (gpointer)); \ diff --git a/glib/glib-typeof.h b/glib/glib-typeof.h index 106859d7b..c3519fa47 100644 --- a/glib/glib-typeof.h +++ b/glib/glib-typeof.h @@ -34,11 +34,10 @@ * This symbol is private. */ #undef glib_typeof -#if (!defined(__cplusplus) || (!defined (_MSVC_LANG) && __cplusplus < 201103L)) && \ +#if !G_CXX_STD_CHECK_VERSION (11) && \ (G_GNUC_CHECK_VERSION(4, 8) || defined(__clang__)) #define glib_typeof(t) __typeof__ (t) -#elif defined(__cplusplus) && \ - (__cplusplus >= 201103L || (defined (_MSVC_LANG) && _MSVC_LANG > 201103L)) && \ +#elif G_CXX_STD_CHECK_VERSION (11) && \ GLIB_VERSION_MIN_REQUIRED >= GLIB_VERSION_2_68 /* C++11 decltype() is close enough for our usage */ #include diff --git a/glib/gmacros.h b/glib/gmacros.h index 36d2238fc..9f69428f3 100644 --- a/glib/gmacros.h +++ b/glib/gmacros.h @@ -64,21 +64,64 @@ #define G_GNUC_EXTENSION #endif +#if !defined (__cplusplus) + +# 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 +# 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 - * Visual Studio does not support __STDC_VERSION__, so we need to check - * whether we are on Visual Studio 2013 or earlier to see that we need to - * say "__inline" in C mode. + * we are using C++, then we can use "inline" directly. * Otherwise, we say "__inline" to avoid the warning. + * Unfortunately Visual Studio does not define __STDC_VERSION__ (if not + * using /std:cXX) so we need to check whether we are on Visual Studio 2013 + * or earlier to see whether we need to say "__inline" in C mode. */ #define G_CAN_INLINE -#ifndef __cplusplus +#ifdef G_C_STD_VERSION # ifdef _MSC_VER # if (_MSC_VER < 1900) # define G_INLINE_DEFINE_NEEDED # endif -# elif !defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199900) +# elif !G_C_STD_CHECK_VERSION (99) # define G_INLINE_DEFINE_NEEDED # endif #endif @@ -819,12 +862,10 @@ #ifndef __GI_SCANNER__ /* The static assert macro really confuses the introspection parser */ #define G_PASTE_ARGS(identifier1,identifier2) identifier1 ## identifier2 #define G_PASTE(identifier1,identifier2) G_PASTE_ARGS (identifier1, identifier2) -#if !defined(__cplusplus) && defined(__STDC_VERSION__) && \ - (__STDC_VERSION__ >= 201112L || g_macro__has_feature(c_static_assert) || g_macro__has_extension(c_static_assert)) +#if (G_C_STD_CHECK_VERSION (11) || \ + g_macro__has_feature(c_static_assert) || g_macro__has_extension(c_static_assert)) #define G_STATIC_ASSERT(expr) _Static_assert (expr, "Expression evaluates to false") -#elif (defined(__cplusplus) && __cplusplus >= 201103L) || \ - (defined(__cplusplus) && defined (_MSC_VER) && (_MSC_VER >= 1600)) || \ - (defined (_MSC_VER) && (_MSC_VER >= 1800)) +#elif G_CXX_STD_CHECK_VERSION (11) #define G_STATIC_ASSERT(expr) static_assert (expr, "Expression evaluates to false") #else #ifdef __COUNTER__ @@ -832,21 +873,21 @@ #else #define G_STATIC_ASSERT(expr) typedef char G_PASTE (_GStaticAssertCompileTimeAssertion_, __LINE__)[(expr) ? 1 : -1] G_GNUC_UNUSED #endif -#endif /* __STDC_VERSION__ */ +#endif /* G_C_STD_CHECK_VERSION (11) */ #define G_STATIC_ASSERT_EXPR(expr) ((void) sizeof (char[(expr) ? 1 : -1])) #endif /* !__GI_SCANNER__ */ /* Provide a string identifying the current code position */ -#if defined(__GNUC__) && (__GNUC__ < 3) && !defined(__cplusplus) +#if defined (__GNUC__) && (__GNUC__ < 3) && !defined (G_CXX_STD_VERSION) #define G_STRLOC __FILE__ ":" G_STRINGIFY (__LINE__) ":" __PRETTY_FUNCTION__ "()" #else #define G_STRLOC __FILE__ ":" G_STRINGIFY (__LINE__) #endif /* Provide a string identifying the current function, non-concatenatable */ -#if defined (__GNUC__) && defined (__cplusplus) +#if defined (__GNUC__) && defined (G_CXX_STD_VERSION) #define G_STRFUNC ((const char*) (__PRETTY_FUNCTION__)) -#elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#elif G_C_STD_CHECK_VERSION (99) #define G_STRFUNC ((const char*) (__func__)) #elif defined (__GNUC__) || (defined(_MSC_VER) && (_MSC_VER > 1300)) #define G_STRFUNC ((const char*) (__FUNCTION__)) @@ -855,7 +896,7 @@ #endif /* Guard C code in headers, while including them from C++ */ -#ifdef __cplusplus +#ifdef G_CXX_STD_VERSION #define G_BEGIN_DECLS extern "C" { #define G_END_DECLS } #else @@ -869,16 +910,14 @@ * defined then the current definition is correct. */ #ifndef NULL -# ifdef __cplusplus -# if __cplusplus >= 201103L -# define NULL (nullptr) -# else -# define NULL (0L) -# endif /* __cplusplus >= 201103L */ -# else /* !__cplusplus */ -# define NULL ((void*) 0) -# endif /* !__cplusplus */ -#elif defined (__cplusplus) && __cplusplus >= 201103L +# if G_CXX_STD_CHECK_VERSION (11) +# define NULL (nullptr) +# elif defined (G_CXX_STD_VERSION) +# define NULL (0L) +# else +# define NULL ((void*) 0) +# endif /* G_CXX_STD_CHECK_VERSION (11) */ +#elif G_CXX_STD_CHECK_VERSION (11) # undef NULL # define NULL (nullptr) #endif @@ -979,7 +1018,7 @@ * * Since: 2.60 */ -#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined(__cplusplus) +#if G_C_STD_CHECK_VERSION (11) #define G_ALIGNOF(type) _Alignof (type) \ GLIB_AVAILABLE_MACRO_IN_2_60 #else @@ -1031,18 +1070,18 @@ * evaluated when a header is included. This results in warnings in third party * code which includes glib.h, even if the third party code doesn’t use the new * macro itself. */ -#if g_macro__has_attribute(__noreturn__) +#if G_CXX_STD_CHECK_VERSION (11) + /* Use ISO C++11 syntax when the compiler supports it. */ +# define G_NORETURN [[noreturn]] +#elif g_macro__has_attribute(__noreturn__) /* For compatibility with G_NORETURN_FUNCPTR on clang, use __attribute__((__noreturn__)), not _Noreturn. */ # define G_NORETURN __attribute__ ((__noreturn__)) #elif defined (_MSC_VER) && (1200 <= _MSC_VER) /* Use MSVC specific syntax. */ # define G_NORETURN __declspec (noreturn) - /* Use ISO C++11 syntax when the compiler supports it. */ -#elif defined (__cplusplus) && __cplusplus >= 201103 -# define G_NORETURN [[noreturn]] /* Use ISO C11 syntax when the compiler supports it. */ -#elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112 +#elif G_C_STD_CHECK_VERSION (11) # define G_NORETURN _Noreturn #else # define G_NORETURN /* empty */ @@ -1110,7 +1149,7 @@ * code which includes glib.h, even if the third party code doesn’t use the new * macro itself. */ #if g_macro__has_attribute(__always_inline__) -# if defined (__cplusplus) && __cplusplus >= 201103L +# if G_CXX_STD_CHECK_VERSION (11) /* Use ISO C++11 syntax when the compiler supports it. */ # define G_ALWAYS_INLINE [[gnu::always_inline]] # else @@ -1154,20 +1193,18 @@ * code which includes glib.h, even if the third party code doesn’t use the new * macro itself. */ #if g_macro__has_attribute(__noinline__) -# if defined (__cplusplus) && __cplusplus >= 201103L +# if G_CXX_STD_CHECK_VERSION (11) /* Use ISO C++11 syntax when the compiler supports it. */ -# define G_NO_INLINE [[gnu::noinline]] +# if defined (__GNUC__) +# define G_NO_INLINE [[gnu::noinline]] +# endif # else # define G_NO_INLINE __attribute__ ((__noinline__)) # endif #elif defined (_MSC_VER) && (1200 <= _MSC_VER) /* Use MSVC specific syntax. */ -# if defined (__cplusplus) && __cplusplus >= 201103L /* Use ISO C++11 syntax when the compiler supports it. */ -# define G_NO_INLINE [[msvc::noinline]] -# else -# define G_NO_INLINE __declspec (noinline) -# endif +# define G_NO_INLINE __declspec (noinline) #else # define G_NO_INLINE /* empty */ #endif diff --git a/glib/gmessages.h b/glib/gmessages.h index 119b72e91..eab6d0678 100644 --- a/glib/gmessages.h +++ b/glib/gmessages.h @@ -291,8 +291,8 @@ void g_warn_message (const char *domain, int line, const char *func, const char *warnexpr) G_ANALYZER_NORETURN; -GLIB_DEPRECATED G_NORETURN +GLIB_DEPRECATED void g_assert_warning (const char *log_domain, const char *file, const int line, @@ -409,7 +409,7 @@ void g_log_structured_standard (const gchar *log_domain, format) #endif #else /* no varargs macros */ -static G_NORETURN void g_error (const gchar *format, ...) G_ANALYZER_NORETURN; +G_NORETURN static void g_error (const gchar *format, ...) G_ANALYZER_NORETURN; static void g_critical (const gchar *format, ...) G_ANALYZER_NORETURN; static inline void diff --git a/glib/gtestutils.h b/glib/gtestutils.h index 4e38eb414..5db453a70 100644 --- a/glib/gtestutils.h +++ b/glib/gtestutils.h @@ -195,7 +195,7 @@ typedef void (*GTestFixtureFunc) (gpointer fixture, } G_STMT_END /* Use nullptr in C++ to catch misuse of these macros. */ -#if defined(__cplusplus) && __cplusplus >= 201100L +#if G_CXX_STD_CHECK_VERSION (11) #define g_assert_null(expr) G_STMT_START { if G_LIKELY ((expr) == nullptr) ; else \ g_assertion_message (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \ "'" #expr "' should be nullptr"); \ @@ -541,8 +541,8 @@ void g_assertion_message (const char *domain, int line, const char *func, const char *message) G_ANALYZER_NORETURN; -GLIB_AVAILABLE_IN_ALL G_NORETURN +GLIB_AVAILABLE_IN_ALL void g_assertion_message_expr (const char *domain, const char *file, int line, diff --git a/glib/gutils.h b/glib/gutils.h index 36e2b50f5..efc691449 100644 --- a/glib/gutils.h +++ b/glib/gutils.h @@ -426,8 +426,7 @@ g_bit_storage_impl (gulong number) # include # define g_abort() abort () #else -GLIB_AVAILABLE_IN_2_50 -G_NORETURN void g_abort (void) G_ANALYZER_NORETURN; +G_NORETURN GLIB_AVAILABLE_IN_2_50 void g_abort (void) G_ANALYZER_NORETURN; #endif #endif diff --git a/glib/tests/cxx.cpp b/glib/tests/cxx.cpp index aa5e0cb57..86bf21b57 100644 --- a/glib/tests/cxx.cpp +++ b/glib/tests/cxx.cpp @@ -19,6 +19,120 @@ #include +#if !defined (G_CXX_STD_VERSION) || !defined (G_CXX_STD_CHECK_VERSION) +#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)); + 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; @@ -31,6 +145,8 @@ test_typeof (void) MyObject *obj2 = g_rc_box_acquire (obj); g_assert_true (obj2 == obj); + G_STATIC_ASSERT (sizeof (glib_typeof (*obj)) == sizeof (glib_typeof (*obj2))); + MyObject *obj3 = g_atomic_pointer_get (&obj2); g_assert_true (obj3 == obj); @@ -186,12 +302,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..89d74d3a5 100644 --- a/glib/tests/macros.c +++ b/glib/tests/macros.c @@ -22,6 +22,87 @@ #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)); +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 test_assert_static (void) @@ -65,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 2af5871ff..a01649b77 100644 --- a/glib/tests/meson.build +++ b/glib/tests/meson.build @@ -3,6 +3,7 @@ glib_tests = { 'asyncqueue' : {}, 'atomic' : { 'c_args' : cc.get_id() == 'gcc' ? ['-Wstrict-aliasing=2'] : [], + 'c_standards': c_standards.keys(), }, 'base64' : {}, 'bitlock' : {}, @@ -48,7 +49,9 @@ glib_tests = { 'keyfile' : {}, 'list' : {}, 'logging' : {}, - 'macros' : {}, + 'macros' : { + 'c_standards': c_standards.keys(), + }, 'mainloop' : {}, 'mappedfile' : {}, 'mapping' : {}, @@ -152,7 +155,9 @@ glib_tests = { 'utf8-pointer' : {}, 'utf8-validate' : {}, 'utf8-misc' : {}, - 'utils' : {}, + 'utils' : { + 'c_standards': c_standards.keys(), + }, 'unicode' : {}, 'unicode-encoding' : {}, 'unicode-normalize': {}, @@ -187,7 +192,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 @@ -294,6 +299,23 @@ if host_machine.system() == 'windows' } endif +foreach test_name, extra_args : glib_tests + foreach std: extra_args.get('c_standards', []) + if c_standards.has_key(std) + 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), + '-D_G_EXPECTED_C_STANDARD="@0@"'.format(std) + ] + extra_args.get('c_args', []), + } + } + endif + endforeach +endforeach + test_env = environment() test_env.set('G_TEST_SRCDIR', meson.current_source_dir()) test_env.set('G_TEST_BUILDDIR', meson.current_build_dir()) diff --git a/gobject/gtype.h b/gobject/gtype.h index dc8fa25d4..f77a1a37b 100644 --- a/gobject/gtype.h +++ b/gobject/gtype.h @@ -412,7 +412,7 @@ G_BEGIN_DECLS * A numerical value which represents the unique identifier of a registered * type. */ -#if GLIB_SIZEOF_SIZE_T != GLIB_SIZEOF_LONG || !defined __cplusplus +#if GLIB_SIZEOF_SIZE_T != GLIB_SIZEOF_LONG || !defined (G_CXX_STD_VERSION) typedef gsize GType; #else /* for historic reasons, C++ links against gulong GTypes */ typedef gulong GType; @@ -2331,7 +2331,8 @@ type_name##_get_type (void) \ /* Only use this in non-C++ on GCC >= 2.7, except for Darwin/ppc64. * See https://bugzilla.gnome.org/show_bug.cgi?id=647145 */ -#if !defined (__cplusplus) && (G_GNUC_CHECK_VERSION(2, 7)) && !(defined (__APPLE__) && defined (__ppc64__)) +#if !defined (G_CXX_STD_VERSION) && (G_GNUC_CHECK_VERSION(2, 7)) && \ + !(defined (__APPLE__) && defined (__ppc64__)) #define _G_DEFINE_BOXED_TYPE_BEGIN(TypeName, type_name, copy_func, free_func) \ static GType type_name##_get_type_once (void); \ \ diff --git a/meson.build b/meson.build index 518de215b..422e30653 100644 --- a/meson.build +++ b/meson.build @@ -10,6 +10,15 @@ project('glib', 'c', ) cc = meson.get_compiler('c') +c_standards = {} + +foreach std : ['90', '99', '11', '17'] + arg = (cc.get_id() == 'msvc' ? '/std:' : '-std=') + 'c' + std + if cc.has_argument(arg) + c_standards += { std: arg } + endif +endforeach + have_cxx = add_languages('cpp', native: false, required: get_option('oss_fuzz').enabled()) if have_cxx cxx = meson.get_compiler('cpp')