public inbox for [email protected]
help / color / mirror / Atom feedFrom: Jelte Fennema-Nio <[email protected]>
To: Peter Eisentraut <[email protected]>
Cc: PostgreSQL Hackers <[email protected]>
Cc: Thomas Munro <[email protected]>
Subject: Re: Make copyObject work in C++
Date: Fri, 27 Feb 2026 17:40:21 +0100
Message-ID: <[email protected]> (raw)
In-Reply-To: <[email protected]>
References: <CAGECzQR21OnnKiZO_1rLWO0-16kg1JBxnVq-wymYW0-_1cUNtg@mail.gmail.com>
<[email protected]>
<[email protected]>
<[email protected]>
<[email protected]>
<[email protected]>
<[email protected]>
<[email protected]>
<ztktlrqatfj4roqhdjurccb3b4mf5hrycybfrzwupt4xmbwjt4@alrtyxhz5sns>
<[email protected]>
<2h2n2gyw2f4ucicbl3drtdkjt2wzf6b2r4wqm7xwks6vpx5j7n@imymv4hkz5jz>
<[email protected]>
<[email protected]>
<[email protected]>
On Fri Feb 20, 2026 at 10:47 AM CET, Jelte Fennema-Nio wrote:
> Makes total sense, I didn't realise decltype and typeof were not quite
> the same thing. Attached is an updated patchset that does that.
Same patchset as before, but now also including a C++ fallback for
__builtin_types_compatible_p.
Attachments:
[text/x-patch] v10-0001-Support-using-copyObject-in-C.patch (7.6K, 2-v10-0001-Support-using-copyObject-in-C.patch)
download | inline diff:
From 077bb1a951c4eb6d61ba2e977c6b09d8d295831a Mon Sep 17 00:00:00 2001
From: Jelte Fennema-Nio <[email protected]>
Date: Fri, 5 Dec 2025 15:37:59 +0100
Subject: [PATCH v10 1/4] Support using copyObject in C++
Calling copyObject in C++ without GNU extensions (e.g. then using
-std=c++11 instead of -std=gnu++11) fails with an error like this:
error: use of undeclared identifier 'typeof'; did you mean 'typeid'
This is due to the C compiler used to compile postgres supporting
typeof, but that function actually not being present in the C++
compiler. This fixes that by explicitely checking for typeof support in
C++ and then either use that, or define typeof ourselves as:
std::remove_reference_t<decltype(x)>
According to the paper that led to adding typeof to the C standard,
that's the C++ equivalent of the C typeof:
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2927.htm#existing-decltype
---
config/c-compiler.m4 | 29 ++++++++++
configure | 54 +++++++++++++++++++
configure.ac | 1 +
meson.build | 25 +++++++++
src/include/c.h | 21 ++++++++
src/include/pg_config.h.in | 4 ++
.../test_cplusplusext/test_cplusplusext.cpp | 2 +
7 files changed, 136 insertions(+)
diff --git a/config/c-compiler.m4 b/config/c-compiler.m4
index 1509dbfa2ab..5b3cbc7e8e8 100644
--- a/config/c-compiler.m4
+++ b/config/c-compiler.m4
@@ -193,6 +193,35 @@ fi])# PGAC_C_TYPEOF
+# PGAC_CXX_TYPEOF
+# ----------------
+# Check if the C++ compiler understands typeof or a variant. Define
+# HAVE_CXX_TYPEOF if so, and define 'pg_cxx_typeof' to the actual key word.
+#
+AC_DEFUN([PGAC_CXX_TYPEOF],
+[AC_CACHE_CHECK(for C++ typeof, pgac_cv_cxx_typeof,
+[pgac_cv_cxx_typeof=no
+AC_LANG_PUSH(C++)
+for pgac_kw in typeof __typeof__; do
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],
+[int x = 0;
+$pgac_kw(x) y;
+y = x;
+return y;])],
+[pgac_cv_cxx_typeof=$pgac_kw])
+ test "$pgac_cv_cxx_typeof" != no && break
+done
+AC_LANG_POP([])])
+if test "$pgac_cv_cxx_typeof" != no; then
+ AC_DEFINE(HAVE_CXX_TYPEOF, 1,
+ [Define to 1 if your C++ compiler understands `typeof' or something similar.])
+ if test "$pgac_cv_cxx_typeof" != typeof; then
+ AC_DEFINE_UNQUOTED(pg_cxx_typeof, $pgac_cv_cxx_typeof, [Define to how the C++ compiler spells `typeof'.])
+ fi
+fi])# PGAC_CXX_TYPEOF
+
+
+
# PGAC_C_TYPES_COMPATIBLE
# -----------------------
# Check if the C compiler understands __builtin_types_compatible_p,
diff --git a/configure b/configure
index a285a6ec3d7..604f9e0100b 100755
--- a/configure
+++ b/configure
@@ -15078,6 +15078,60 @@ _ACEOF
fi
fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ typeof" >&5
+$as_echo_n "checking for C++ typeof... " >&6; }
+if ${pgac_cv_cxx_typeof+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ pgac_cv_cxx_typeof=no
+ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+
+for pgac_kw in typeof __typeof__; do
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+int x = 0;
+$pgac_kw(x) y;
+y = x;
+return y;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+ pgac_cv_cxx_typeof=$pgac_kw
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ test "$pgac_cv_cxx_typeof" != no && break
+done
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $pgac_cv_cxx_typeof" >&5
+$as_echo "$pgac_cv_cxx_typeof" >&6; }
+if test "$pgac_cv_cxx_typeof" != no; then
+
+$as_echo "#define HAVE_CXX_TYPEOF 1" >>confdefs.h
+
+ if test "$pgac_cv_cxx_typeof" != typeof; then
+
+cat >>confdefs.h <<_ACEOF
+#define pg_cxx_typeof $pgac_cv_cxx_typeof
+_ACEOF
+
+ fi
+fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for __builtin_types_compatible_p" >&5
$as_echo_n "checking for __builtin_types_compatible_p... " >&6; }
if ${pgac_cv__types_compatible+:} false; then :
diff --git a/configure.ac b/configure.ac
index 476a76c7991..93ecf41447b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1732,6 +1732,7 @@ PGAC_PRINTF_ARCHETYPE
PGAC_CXX_PRINTF_ARCHETYPE
PGAC_C_STATEMENT_EXPRESSIONS
PGAC_C_TYPEOF
+PGAC_CXX_TYPEOF
PGAC_C_TYPES_COMPATIBLE
PGAC_C_BUILTIN_CONSTANT_P
PGAC_C_BUILTIN_OP_OVERFLOW
diff --git a/meson.build b/meson.build
index 5122706477d..9cc95f0be5f 100644
--- a/meson.build
+++ b/meson.build
@@ -2953,6 +2953,31 @@ int main(void)
endif
endforeach
+# Check if the C++ compiler understands typeof or a variant.
+if have_cxx
+ foreach kw : ['typeof', '__typeof__']
+ if cxx.compiles('''
+int main(void)
+{
+ int x = 0;
+ @0@(x) y;
+ y = x;
+ return y;
+}
+'''.format(kw),
+ name: 'C++ ' + kw,
+ args: test_c_args, include_directories: postgres_inc)
+
+ cdata.set('HAVE_CXX_TYPEOF', 1)
+ if kw != 'typeof'
+ cdata.set('pg_cxx_typeof', kw)
+ endif
+
+ break
+ endif
+ endforeach
+endif
+
# MSVC doesn't cope well with defining restrict to __restrict, the
# spelling it understands, because it conflicts with
diff --git a/src/include/c.h b/src/include/c.h
index fb0ea1bc680..a4fee84398d 100644
--- a/src/include/c.h
+++ b/src/include/c.h
@@ -424,6 +424,27 @@
#define unlikely(x) ((x) != 0)
#endif
+/*
+ * Provide typeof in C++ for C++ compilers that don't support typeof natively.
+ * It might be spelled __typeof__ instead of typeof, in which case
+ * pg_cxx_typeof provides that mapping. If neither is supported, we can use
+ * decltype, but to make it equivalent to C's typeof, we need to remove
+ * references from the result [1]. Also ensure HAVE_TYPEOF is set so that
+ * typeof-dependent code is always enabled in C++ mode.
+ *
+ * [1]: https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2927.htm#existing-decltype
+ */
+#if defined(__cplusplus)
+#ifdef pg_cxx_typeof
+#define typeof(x) pg_cxx_typeof(x)
+#elif !defined(HAVE_CXX_TYPEOF)
+#define typeof(x) std::remove_reference_t<decltype(x)>
+#endif
+#ifndef HAVE_TYPEOF
+#define HAVE_TYPEOF 1
+#endif
+#endif
+
/*
* CppAsString
* Convert the argument to a string, using the C preprocessor.
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index a0bd84376e7..a970577ae3e 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -451,6 +451,10 @@
/* Define to 1 if your compiler understands `typeof' or something similar. */
#undef HAVE_TYPEOF
+/* Define to 1 if your C++ compiler understands `typeof' or something similar.
+ */
+#undef HAVE_CXX_TYPEOF
+
/* Define to 1 if you have the <uchar.h> header file. */
#undef HAVE_UCHAR_H
diff --git a/src/test/modules/test_cplusplusext/test_cplusplusext.cpp b/src/test/modules/test_cplusplusext/test_cplusplusext.cpp
index eb129dd15d4..ea04a761184 100644
--- a/src/test/modules/test_cplusplusext/test_cplusplusext.cpp
+++ b/src/test/modules/test_cplusplusext/test_cplusplusext.cpp
@@ -37,6 +37,7 @@ test_cplusplus_add(PG_FUNCTION_ARGS)
int32 a = PG_GETARG_INT32(0);
int32 b = PG_GETARG_INT32(1);
RangeTblRef *node = makeNode(RangeTblRef);
+ RangeTblRef *copy = copyObject(node);
List *list = list_make1(node);
foreach_ptr(RangeTblRef, rtr, list)
@@ -54,6 +55,7 @@ test_cplusplus_add(PG_FUNCTION_ARGS)
list_free(list);
pfree(node);
+ pfree(copy);
switch (a)
{
base-commit: 284925508ae685a63ee056f89a336caecab64a63
--
2.53.0
[text/x-patch] v10-0002-Use-typeof-everywhere-instead-of-compiler-specif.patch (2.3K, 3-v10-0002-Use-typeof-everywhere-instead-of-compiler-specif.patch)
download | inline diff:
From c8fd6666836c7696c49708e1afb785cad3b55f13 Mon Sep 17 00:00:00 2001
From: Jelte Fennema-Nio <[email protected]>
Date: Mon, 8 Dec 2025 08:13:51 +0100
Subject: [PATCH v10 2/4] Use typeof everywhere instead of compiler specific
spellings
We define typeof ourselves as __typeof__ if it does not exist. So let's
actually use that for consistency. The meson/autoconf checks for
__builtin_types_compatible_p still use __typeof__ though, because there
we have not redefined it.
---
src/include/c.h | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/include/c.h b/src/include/c.h
index a4fee84398d..df01acdf6ba 100644
--- a/src/include/c.h
+++ b/src/include/c.h
@@ -1008,10 +1008,10 @@ pg_noreturn extern void ExceptionalCondition(const char *conditionName,
*/
#ifdef HAVE__BUILTIN_TYPES_COMPATIBLE_P
#define StaticAssertVariableIsOfType(varname, typename) \
- StaticAssertDecl(__builtin_types_compatible_p(__typeof__(varname), typename), \
+ StaticAssertDecl(__builtin_types_compatible_p(typeof(varname), typename), \
CppAsString(varname) " does not have type " CppAsString(typename))
#define StaticAssertVariableIsOfTypeMacro(varname, typename) \
- (StaticAssertExpr(__builtin_types_compatible_p(__typeof__(varname), typename), \
+ (StaticAssertExpr(__builtin_types_compatible_p(typeof(varname), typename), \
CppAsString(varname) " does not have type " CppAsString(typename)))
#else /* !HAVE__BUILTIN_TYPES_COMPATIBLE_P */
#define StaticAssertVariableIsOfType(varname, typename) \
@@ -1275,11 +1275,11 @@ typedef struct PGAlignedXLogBlock PGAlignedXLogBlock;
#define unvolatize(underlying_type, expr) const_cast<underlying_type>(expr)
#elif defined(HAVE__BUILTIN_TYPES_COMPATIBLE_P)
#define unconstify(underlying_type, expr) \
- (StaticAssertExpr(__builtin_types_compatible_p(__typeof(expr), const underlying_type), \
+ (StaticAssertExpr(__builtin_types_compatible_p(typeof(expr), const underlying_type), \
"wrong cast"), \
(underlying_type) (expr))
#define unvolatize(underlying_type, expr) \
- (StaticAssertExpr(__builtin_types_compatible_p(__typeof(expr), volatile underlying_type), \
+ (StaticAssertExpr(__builtin_types_compatible_p(typeof(expr), volatile underlying_type), \
"wrong cast"), \
(underlying_type) (expr))
#else
--
2.53.0
[text/x-patch] v10-0003-Make-unconstify-and-unvolatize-use-StaticAssertV.patch (1.7K, 4-v10-0003-Make-unconstify-and-unvolatize-use-StaticAssertV.patch)
download | inline diff:
From 8abacdb5305619a19fb3dc7eaa53d4cc4d7d805e Mon Sep 17 00:00:00 2001
From: Jelte Fennema-Nio <[email protected]>
Date: Thu, 19 Feb 2026 23:55:43 +0100
Subject: [PATCH v10 3/4] Make unconstify and unvolatize use
StaticAssertVariableIsOfTypeMacro
The unconstify and unvolatize macros had an almost identical assertion
as was already defined in StaticAssertVariableIsOfTypeMacro, only it had
a less useful error message and didn't have a sizeof fallback.
---
src/include/c.h | 13 +++----------
1 file changed, 3 insertions(+), 10 deletions(-)
diff --git a/src/include/c.h b/src/include/c.h
index df01acdf6ba..c92b214f6b0 100644
--- a/src/include/c.h
+++ b/src/include/c.h
@@ -1273,20 +1273,13 @@ typedef struct PGAlignedXLogBlock PGAlignedXLogBlock;
#if defined(__cplusplus)
#define unconstify(underlying_type, expr) const_cast<underlying_type>(expr)
#define unvolatize(underlying_type, expr) const_cast<underlying_type>(expr)
-#elif defined(HAVE__BUILTIN_TYPES_COMPATIBLE_P)
+#else
#define unconstify(underlying_type, expr) \
- (StaticAssertExpr(__builtin_types_compatible_p(typeof(expr), const underlying_type), \
- "wrong cast"), \
+ (StaticAssertVariableIsOfTypeMacro(expr, const underlying_type), \
(underlying_type) (expr))
#define unvolatize(underlying_type, expr) \
- (StaticAssertExpr(__builtin_types_compatible_p(typeof(expr), volatile underlying_type), \
- "wrong cast"), \
+ (StaticAssertVariableIsOfTypeMacro(expr, volatile underlying_type), \
(underlying_type) (expr))
-#else
-#define unconstify(underlying_type, expr) \
- ((underlying_type) (expr))
-#define unvolatize(underlying_type, expr) \
- ((underlying_type) (expr))
#endif
/*
--
2.53.0
[text/x-patch] v10-0004-Add-C-version-of-__builtin_types_compatible_p.patch (3.7K, 5-v10-0004-Add-C-version-of-__builtin_types_compatible_p.patch)
download | inline diff:
From 437a8aece153c5dc24c0e2708c034fe1718ea3bf Mon Sep 17 00:00:00 2001
From: Jelte Fennema-Nio <[email protected]>
Date: Fri, 13 Feb 2026 09:39:34 +0100
Subject: [PATCH v10 4/4] Add C++ version of __builtin_types_compatible_p
StaticAssertVariableIsOfType would fail to compile in C++ extensions if
meson/autoconf has detected HAVE__BUILTIN_TYPES_COMPATIBLE_P to be true
for the C compiler. Basically equivalent to the PG_PRINTF_ATTRIBUTE
situation in 0909380e4c9.
This fixes that problem by defining __builtin_types_compatible_p in C++
using an equivalent STL constructs.
---
src/include/c.h | 22 +++++++++++++++++++
.../test_cplusplusext/test_cplusplusext.cpp | 20 ++++++++++++++++-
2 files changed, 41 insertions(+), 1 deletion(-)
diff --git a/src/include/c.h b/src/include/c.h
index c92b214f6b0..ba629a3503b 100644
--- a/src/include/c.h
+++ b/src/include/c.h
@@ -80,6 +80,12 @@
#ifdef HAVE_XLOCALE_H
#include <xlocale.h>
#endif
+#ifdef __cplusplus
+extern "C++"
+{
+#include <type_traits>
+}
+#endif
#ifdef ENABLE_NLS
#include <libintl.h>
#endif
@@ -443,7 +449,23 @@
#ifndef HAVE_TYPEOF
#define HAVE_TYPEOF 1
#endif
+/*
+ * Provide __builtin_types_compatible_p for C++ by comparing types with
+ * std::is_same after stripping cv-qualifiers. The second branch handles
+ * GCC's special case where an incomplete array (e.g. int[]) is considered
+ * compatible with a complete array of the same element type (e.g. int[3]).
+ */
+#define __builtin_types_compatible_p(x, y) \
+ (std::is_same<std::remove_cv_t<x>, std::remove_cv_t<y>>::value || \
+ (std::is_array<x>::value && std::is_array<y>::value && \
+ (std::extent<x>::value == 0 || std::extent<y>::value == 0) && \
+ std::is_same<std::remove_cv_t<std::remove_extent_t<x>>, \
+ std::remove_cv_t<std::remove_extent_t<y>>>::value))
+#ifndef HAVE__BUILTIN_TYPES_COMPATIBLE_P
+#define HAVE__BUILTIN_TYPES_COMPATIBLE_P 1
#endif
+#endif
+
/*
* CppAsString
diff --git a/src/test/modules/test_cplusplusext/test_cplusplusext.cpp b/src/test/modules/test_cplusplusext/test_cplusplusext.cpp
index ea04a761184..936ca422c76 100644
--- a/src/test/modules/test_cplusplusext/test_cplusplusext.cpp
+++ b/src/test/modules/test_cplusplusext/test_cplusplusext.cpp
@@ -34,11 +34,14 @@ StaticAssertDecl(sizeof(int32) == 4, "int32 should be 4 bytes");
extern "C" Datum
test_cplusplus_add(PG_FUNCTION_ARGS)
{
- int32 a = PG_GETARG_INT32(0);
+ const int32 a = PG_GETARG_INT32(0);
int32 b = PG_GETARG_INT32(1);
+ const char *p = "";
RangeTblRef *node = makeNode(RangeTblRef);
RangeTblRef *copy = copyObject(node);
List *list = list_make1(node);
+ const int32 int_array[3] = {0};
+ extern int32 incomplete_array[];
foreach_ptr(RangeTblRef, rtr, list)
{
@@ -52,6 +55,21 @@ test_cplusplus_add(PG_FUNCTION_ARGS)
StaticAssertStmt(sizeof(int32) == 4, "int32 should be 4 bytes");
(void) StaticAssertExpr(sizeof(int64) == 8, "int64 should be 8 bytes");
+ StaticAssertVariableIsOfType(a, int32);
+ StaticAssertVariableIsOfType(a, const int32);
+ StaticAssertVariableIsOfType(b, const int32);
+ StaticAssertVariableIsOfType(int_array, int32[3]);
+ StaticAssertVariableIsOfType(int_array, const int32[3]);
+ StaticAssertVariableIsOfType(p, const char *);
+ StaticAssertVariableIsOfTypeMacro(a, int32);
+ StaticAssertVariableIsOfTypeMacro(a, const int32);
+ StaticAssertVariableIsOfTypeMacro(b, int32);
+ StaticAssertVariableIsOfTypeMacro(int_array, int32[3]);
+ StaticAssertVariableIsOfTypeMacro(int_array, const int32[3]);
+ StaticAssertVariableIsOfTypeMacro(p, const char *);
+ /* incomplete array matches complete array type */
+ StaticAssertVariableIsOfType(incomplete_array, int32[3]);
+ StaticAssertVariableIsOfTypeMacro(incomplete_array, int32[3]);
list_free(list);
pfree(node);
--
2.53.0
view thread (27+ messages) latest in thread
reply
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: [email protected]
Cc: [email protected], [email protected], [email protected], [email protected]
Subject: Re: Make copyObject work in C++
In-Reply-To: <[email protected]>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox