[glib/c-cxx-std-versions: 1/7] gmacros: Define G_CXX_STD_VERSION and check macros
- From: Marco Trevisan <marcotrevi src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib/c-cxx-std-versions: 1/7] gmacros: Define G_CXX_STD_VERSION and check macros
- Date: Thu, 20 Oct 2022 14:05:31 +0000 (UTC)
commit 0372ffe97bade4ec4684daaa9550b287796d9f8f
Author: Marco Trevisan (TreviƱo) <mail 3v1n0 net>
Date: Wed Sep 14 01:03:22 2022 +0200
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
docs/reference/glib/glib-sections.txt.in | 4 ++
glib/docs.c | 38 ++++++++++
glib/gmacros.h | 25 +++++++
glib/tests/cxx.cpp | 118 ++++++++++++++++++++++++++++++-
glib/tests/macros.c | 6 ++
glib/tests/meson.build | 2 +-
6 files changed, 189 insertions(+), 4 deletions(-)
---
diff --git a/docs/reference/glib/glib-sections.txt.in b/docs/reference/glib/glib-sections.txt.in
index 90b0f32585..144e7077b0 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
+<SUBSECTION>
+G_CXX_STD_VERSION
+G_CXX_STD_CHECK_VERSION
+
<SUBSECTION>
G_DEPRECATED
G_DEPRECATED_FOR
diff --git a/glib/docs.c b/glib/docs.c
index f93ce80483..a96e067fbe 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.
+ *
+ * |[<!-- 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
diff --git a/glib/gmacros.h b/glib/gmacros.h
index f9e20407ee..a915a8b7a2 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 6ac60791c8..e99f6f21c0 100644
--- a/glib/tests/cxx.cpp
+++ b/glib/tests/cxx.cpp
@@ -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;
@@ -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 cbbc0f6def..b18f3a3a6d 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 8b5c58b8c5..429218cf54 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
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]