On Thu Feb 19, 2026 at 9:31 AM CET, Peter Eisentraut wrote:
What I suggest we should do here is:

1. Add a configure test that checks if the C++ compiler supports typeof.

2. If not, #define typeof to the above expression.

Then code can continue to use typeof unchanged.

Makes total sense, I didn't realise decltype and typeof were not quite
the same thing. Attached is an updated patchset that does that.

It also includes a patch that improves unconstify and unvolatize by
using StaticAssertVariableIsOfTypeMacro instead of a custom version of
that assertion. (Like I said, in a future patch I intend to make
StaticAssertVariableIsOfTypeMacro work in C++ as well, but this seemed
like a good improvement anyway)
From 1ff9279f3acb207e23e8b20b23429283b81a94fb Mon Sep 17 00:00:00 2001
From: Jelte Fennema-Nio <[email protected]>
Date: Fri, 5 Dec 2025 15:37:59 +0100
Subject: [PATCH v9 1/3] 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 e1a08129974..3995a16fd1c 100755
--- a/configure
+++ b/configure
@@ -14968,6 +14968,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 cc85c233c03..754dc50d6ba 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1716,6 +1716,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 055e96315d0..06ab917365e 100644
--- a/meson.build
+++ b/meson.build
@@ -2882,6 +2882,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 7ee4751992f..c3b3e9eea4e 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 3824a5571bb..0059408569d 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -454,6 +454,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 8c2eabcca43..5e6c8f85f6f 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);
 
 	PG_RETURN_INT32(a + b);
 }

base-commit: ba401828c194c17031a6a2dedd97bc0defd80d4b
-- 
2.53.0

From c0537483fbdace6967df3aa1b76f1db6c66a5883 Mon Sep 17 00:00:00 2001
From: Jelte Fennema-Nio <[email protected]>
Date: Mon, 8 Dec 2025 08:13:51 +0100
Subject: [PATCH v9 2/3] 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 c3b3e9eea4e..07e931a6c89 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

From 37021f7b2cae580ca348c8600f9644f928b48232 Mon Sep 17 00:00:00 2001
From: Jelte Fennema-Nio <[email protected]>
Date: Thu, 19 Feb 2026 23:55:43 +0100
Subject: [PATCH v9 3/3] 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 07e931a6c89..5fa366914d8 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

Reply via email to