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
