On Sun Jan 25, 2026 at 9:06 PM CET, Andres Freund wrote:
Named args make that easier in two ways: First, only extensions using the
to-be-removed option will fail. Second, removal of options reliably generates
errors, rather than bogusly use one field for another, just because the types
are compatible.

After discussing the topic in-person with Peter at FOSDEM. We agreed
that the best road forward was to not bother with MSVC for now. No-one
has actually expressed an interest in being able to build C++ extension
using MSVC, and the effort to support it is both non-trivial and not
without downsides to the rest of the codebase. We can always come back
to this later, possibly requiring C++20 on MSVC.

So I've removed that patch and now this patchset its goal is to improve
compatibiltity with the C++ flavor of GCC and Clang.

Patch 1 and 2 add some more macro calls to our test C++ extension. These
macros already work in GCC and Clang, this is purely to test for future
regressinos.

Patch 3 makes copyObject work when using GCC or Clang with -std=c++11 by
introducing pg_exprtype.

Patch 4 starts using pg_exprtype in more places.

I'm also working on some patches to support
StaticAssertVariableIsOfType, but I've run into some ICE compiler errors
of MSVC 2019.
From 8a5bbbd0e73edafc340c938ec6e234384dbe672a Mon Sep 17 00:00:00 2001
From: Jelte Fennema-Nio <[email protected]>
Date: Fri, 13 Feb 2026 09:14:47 +0100
Subject: [PATCH v8 1/6] Test List macros in C++ extensions

All of these macros already work in C++ with Clang and GCC (the only
compilers we're currently testing C++ extension support for). This adds
a regression test for them in our test C++ extension, so we can safely
change their implementation without accidentally breaking C++. Some of
the List macros didn't work in C++ in the past (d5ca15ee5), this would
have caught that.
---
 .../test_cplusplusext/test_cplusplusext.cpp     | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/src/test/modules/test_cplusplusext/test_cplusplusext.cpp b/src/test/modules/test_cplusplusext/test_cplusplusext.cpp
index 435937c00d2..f1a2ab7f2bf 100644
--- a/src/test/modules/test_cplusplusext/test_cplusplusext.cpp
+++ b/src/test/modules/test_cplusplusext/test_cplusplusext.cpp
@@ -17,6 +17,8 @@
 extern "C" {
 #include "postgres.h"
 #include "fmgr.h"
+#include "nodes/pg_list.h"
+#include "nodes/primnodes.h"
 
 PG_MODULE_MAGIC;
 
@@ -32,6 +34,21 @@ test_cplusplus_add(PG_FUNCTION_ARGS)
 {
 	int32		a = PG_GETARG_INT32(0);
 	int32		b = PG_GETARG_INT32(1);
+	RangeTblRef *node = makeNode(RangeTblRef);
+	List	   *list = list_make1(node);
+
+	foreach_ptr(RangeTblRef, rtr, list)
+	{
+		(void) rtr;
+	}
+
+	foreach_node(RangeTblRef, rtr, list)
+	{
+		(void) rtr;
+	}
+
+	list_free(list);
+	pfree(node);
 
 	PG_RETURN_INT32(a + b);
 }

base-commit: d7edcec35c7c28edb3bf283dfe9c892b042ca158
-- 
2.52.0

From 8f9d2a63bcdf30a445cb24c5453f34d8f54a4b29 Mon Sep 17 00:00:00 2001
From: Jelte Fennema-Nio <[email protected]>
Date: Sat, 17 Jan 2026 14:51:36 +0100
Subject: [PATCH v8 2/6] Test most StaticAssert macros in C++ extensions

Most of the StaticAssert macros already worked in C++ with Clang and GCC
(the only compilers we're currently testing C++ extension support for).
This adds a regression test for them in our test C++ extension, so we
can safely change their implementation without accidentally breaking
C++.

The only macros that StaticAssert macros that don't work yet are the
StaticAssertVariableIsOfType and StaticAssertVariableIsOfTypeMacro.
These will be added in a follow on commit.
---
 src/test/modules/test_cplusplusext/test_cplusplusext.cpp | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/src/test/modules/test_cplusplusext/test_cplusplusext.cpp b/src/test/modules/test_cplusplusext/test_cplusplusext.cpp
index f1a2ab7f2bf..8c2eabcca43 100644
--- a/src/test/modules/test_cplusplusext/test_cplusplusext.cpp
+++ b/src/test/modules/test_cplusplusext/test_cplusplusext.cpp
@@ -25,6 +25,8 @@ PG_MODULE_MAGIC;
 PG_FUNCTION_INFO_V1(test_cplusplus_add);
 }
 
+StaticAssertDecl(sizeof(int32) == 4, "int32 should be 4 bytes");
+
 /*
  * Simple function that returns the sum of two integers.  This verifies that
  * C++ extension modules can be loaded and called correctly at runtime.
@@ -47,6 +49,9 @@ test_cplusplus_add(PG_FUNCTION_ARGS)
 		(void) rtr;
 	}
 
+	StaticAssertStmt(sizeof(int32) == 4, "int32 should be 4 bytes");
+	(void) StaticAssertExpr(sizeof(int64) == 8, "int64 should be 8 bytes");
+
 	list_free(list);
 	pfree(node);
 
-- 
2.52.0

From cb57b403ee7819f7512a5e5edcd4ce1cb876ee46 Mon Sep 17 00:00:00 2001
From: Jelte Fennema-Nio <[email protected]>
Date: Fri, 5 Dec 2025 15:37:59 +0100
Subject: [PATCH v8 3/6] 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 supporting used to compile postgres
supporting typeof, but that function actually not being present in the
C++ compiler. This fixes that by defining pg_exprtype which maps to
typeof or decltype depending on whether it's a C or C++ compiler. While
pg_typeof would have been a more natural name, that name is already
taken in our codebase as the implementation of the pg_typeof UDF.
---
 src/include/c.h                                     | 13 +++++++++++++
 src/include/nodes/nodes.h                           |  4 ++--
 .../modules/test_cplusplusext/test_cplusplusext.cpp |  2 ++
 3 files changed, 17 insertions(+), 2 deletions(-)

diff --git a/src/include/c.h b/src/include/c.h
index 3fc09ec1e4a..9f418e432ea 100644
--- a/src/include/c.h
+++ b/src/include/c.h
@@ -412,6 +412,19 @@
 #define unlikely(x) ((x) != 0)
 #endif
 
+/*
+ * pg_exprtype
+ *		Get the type of an expression at compile time.
+ *
+ * In C++ we use decltype since typeof is not standard C++, while in C we use
+ * typeof when available.
+ */
+#if defined(__cplusplus)
+#define pg_exprtype(x) decltype(x)
+#elif defined(HAVE_TYPEOF)
+#define pg_exprtype(x) typeof(x)
+#endif
+
 /*
  * CppAsString
  *		Convert the argument to a string, using the C preprocessor.
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 59a7df31aba..a03c85f1b01 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -226,8 +226,8 @@ extern int16 *readAttrNumberCols(int numCols);
 extern void *copyObjectImpl(const void *from);
 
 /* cast result back to argument type, if supported by compiler */
-#ifdef HAVE_TYPEOF
-#define copyObject(obj) ((typeof(obj)) copyObjectImpl(obj))
+#ifdef pg_exprtype
+#define copyObject(obj) ((pg_exprtype(obj)) copyObjectImpl(obj))
 #else
 #define copyObject(obj) copyObjectImpl(obj)
 #endif
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);
 }
-- 
2.52.0

From d2986a90548f5dce7fbd6d0e09d7d881f15e3e53 Mon Sep 17 00:00:00 2001
From: Jelte Fennema-Nio <[email protected]>
Date: Mon, 8 Dec 2025 08:13:51 +0100
Subject: [PATCH v8 4/6] Use pg_exprtype instead of typeof

The previous commit introduced pg_exprtype. This starts using that in a
few more places.
---
 src/include/c.h            | 8 ++++----
 src/include/utils/relptr.h | 8 ++++----
 2 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/src/include/c.h b/src/include/c.h
index 9f418e432ea..b448a4a8775 100644
--- a/src/include/c.h
+++ b/src/include/c.h
@@ -978,10 +978,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(pg_exprtype(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(pg_exprtype(varname), typename), \
 	 CppAsString(varname) " does not have type " CppAsString(typename)))
 #else							/* !HAVE__BUILTIN_TYPES_COMPATIBLE_P */
 #define StaticAssertVariableIsOfType(varname, typename) \
@@ -1245,11 +1245,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(pg_exprtype(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(pg_exprtype(expr), volatile underlying_type), \
 					  "wrong cast"), \
 	 (underlying_type) (expr))
 #else
diff --git a/src/include/utils/relptr.h b/src/include/utils/relptr.h
index 94975f2f237..2df6c673017 100644
--- a/src/include/utils/relptr.h
+++ b/src/include/utils/relptr.h
@@ -38,10 +38,10 @@
 #define relptr_declare(type, relptrtype) \
 	typedef relptr(type) relptrtype
 
-#ifdef HAVE_TYPEOF
+#ifdef pg_exprtype
 #define relptr_access(base, rp) \
 	(StaticAssertVariableIsOfTypeMacro(base, char *), \
-	 (typeof((rp).relptr_type)) ((rp).relptr_off == 0 ? NULL : \
+	 (pg_exprtype((rp).relptr_type)) ((rp).relptr_off == 0 ? NULL : \
 		(base) + (rp).relptr_off - 1))
 #else
 #define relptr_access(base, rp) \
@@ -68,10 +68,10 @@ relptr_store_eval(char *base, char *val)
 	}
 }
 
-#ifdef HAVE_TYPEOF
+#ifdef pg_exprtype
 #define relptr_store(base, rp, val) \
 	(StaticAssertVariableIsOfTypeMacro(base, char *), \
-	 StaticAssertVariableIsOfTypeMacro(val, typeof((rp).relptr_type)), \
+	 StaticAssertVariableIsOfTypeMacro(val, pg_exprtype((rp).relptr_type)), \
 	 (rp).relptr_off = relptr_store_eval((base), (char *) (val)))
 #else
 #define relptr_store(base, rp, val) \
-- 
2.52.0

Reply via email to