gmacros: Define G_CXX_STD_VERSION and check macros

Sadly, in C++ there's not an universal way to get what language standard
is used to compile GLib-based programs, in fact while most compilers
relies on `__cplusplus`, MSVC is defining that, but it does not use it
to expose such information (unless `/Zc:__cplusplus` arg is used).
On the other side, MSVC reports the language standard via _MSVC_LANG [1].

This complication makes us defining some macros in a very complex way
(such as glib_typeof()), because we need to perform many checks just to
understand if a C++ compiler is used and what standard is expecting.

To avoid this, define multiple macros that can be used to figure out
what C++ standard is being used.

[1] https://docs.microsoft.com/en-us/cpp/build/reference/zc-cplusplus?view=msvc-170
This commit is contained in:
Marco Trevisan (Treviño) 2022-09-14 01:03:22 +02:00
parent 054b96fd09
commit f9845abe39
6 changed files with 187 additions and 2 deletions

View File

@ -410,6 +410,10 @@ G_HAVE_GNUC_VISIBILITY
G_GNUC_INTERNAL
G_GNUC_MAY_ALIAS
<SUBSECTION>
G_CXX_STD_VERSION
G_CXX_STD_CHECK_VERSION
<SUBSECTION>
G_DEPRECATED
G_DEPRECATED_FOR

View File

@ -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.
*
* |[<!-- language="C" -->
* #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

View File

@ -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

View File

@ -19,6 +19,115 @@
#include <glib.h>
#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<void *>(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);

View File

@ -22,6 +22,12 @@
#include <glib.h>
#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)

View File

@ -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