From 63397ed786c0feb42f9a29989f8c53fe0c9c07ec Mon Sep 17 00:00:00 2001
From: Jelte Fennema-Nio <postgres@jeltef.nl>
Date: Fri, 2 Jan 2026 22:31:05 +0100
Subject: [PATCH v7 1/5] Support building C++ extensions with MSVC

In 476b35d4e31 a C++ test extension module was added. This test was
disabled for MSVC, because it failed to compile it in any C++ version
lower than C++20. The reason for that is our usage of designated
initializers in PG_MODULE_MAGIC_DATA (which was done in 9324c8c580).
Designated initialzers are only part of C++ from the C++20 standard
(although clang and gcc implemented support way earlier).

This reverts to using positional initializers in PG_MODULE_MAGIC_DATA so
that its possible to write C++ extensions in standard C++11. Sadly that
means that using designated initializers in C++20 is still not allowed
in PG_MODULE_MAGIC_EXT because mixing designated an positional
initializers is a C only feature. This restriction for C++ extensions is
now documented and tested.
---
 doc/src/sgml/xfunc.sgml                              |  6 ++++++
 meson.build                                          |  4 ++++
 src/include/fmgr.h                                   | 12 +++++++-----
 .../test_cplusplusext/expected/test_cplusplusext.out |  7 +++++++
 src/test/modules/test_cplusplusext/meson.build       |  5 -----
 .../test_cplusplusext/sql/test_cplusplusext.sql      |  3 +++
 .../modules/test_cplusplusext/test_cplusplusext.cpp  |  2 +-
 7 files changed, 28 insertions(+), 11 deletions(-)

diff --git a/doc/src/sgml/xfunc.sgml b/doc/src/sgml/xfunc.sgml
index 70e815b8a2c..143f87a253a 100644
--- a/doc/src/sgml/xfunc.sgml
+++ b/doc/src/sgml/xfunc.sgml
@@ -1973,6 +1973,12 @@ PG_MODULE_MAGIC_EXT(
     .name = "my_module_name",
     .version = "1.2.3"
 );
+</programlisting>
+
+    In C++ code, use positional arguments instead of designated initializers:
+
+<programlisting>
+PG_MODULE_MAGIC_EXT("my_module_name", "1.2.3");
 </programlisting>
 
     Subsequently the name and version can be examined via
diff --git a/meson.build b/meson.build
index df907b62da3..056412890b5 100644
--- a/meson.build
+++ b/meson.build
@@ -2238,6 +2238,10 @@ if cc.get_id() == 'msvc'
     '/w24777', # 'function' : format string 'string' requires an argument of type 'type1', but variadic argument number has type 'type2' [like -Wformat]
   ]
 
+  cxxflags_warn += [
+    '/wd4200', # nonstandard extension used: zero-sized array in struct/union
+  ]
+
   cppflags += [
     '/DWIN32',
     '/DWINDOWS',
diff --git a/src/include/fmgr.h b/src/include/fmgr.h
index eabbc78b280..1ab8749cee1 100644
--- a/src/include/fmgr.h
+++ b/src/include/fmgr.h
@@ -497,13 +497,15 @@ typedef struct
 }
 
 /*
- * Macro to fill a magic block.  If any arguments are given, they should
- * be field initializers.
+ * Macro to fill a magic block.  If any arguments are given, they should be
+ * field initializers. These can be designated initialzers, or non-designated
+ * initializers. If they're non-designated, they are applied after the ABI
+ * fields.
  */
 #define PG_MODULE_MAGIC_DATA(...) \
 { \
-	.len = sizeof(Pg_magic_struct), \
-	.abi_fields = PG_MODULE_ABI_DATA, \
+	sizeof(Pg_magic_struct), \
+	PG_MODULE_ABI_DATA, \
 	__VA_ARGS__ \
 }
 
@@ -524,7 +526,7 @@ extern PGDLLEXPORT const Pg_magic_struct *PG_MAGIC_FUNCTION_NAME(void); \
 const Pg_magic_struct * \
 PG_MAGIC_FUNCTION_NAME(void) \
 { \
-	static const Pg_magic_struct Pg_magic_data = PG_MODULE_MAGIC_DATA(.name = NULL); \
+	static const Pg_magic_struct Pg_magic_data = PG_MODULE_MAGIC_DATA(); \
 	return &Pg_magic_data; \
 } \
 extern int no_such_variable
diff --git a/src/test/modules/test_cplusplusext/expected/test_cplusplusext.out b/src/test/modules/test_cplusplusext/expected/test_cplusplusext.out
index ab0b04b5c5e..25600cfd1b4 100644
--- a/src/test/modules/test_cplusplusext/expected/test_cplusplusext.out
+++ b/src/test/modules/test_cplusplusext/expected/test_cplusplusext.out
@@ -5,3 +5,10 @@ SELECT test_cplusplus_add(1, 2);
                   3
 (1 row)
 
+SELECT module_name, version FROM pg_get_loaded_modules()
+  WHERE module_name = 'test_cplusplusext';
+    module_name    | version 
+-------------------+---------
+ test_cplusplusext | 1.2
+(1 row)
+
diff --git a/src/test/modules/test_cplusplusext/meson.build b/src/test/modules/test_cplusplusext/meson.build
index d13210ca593..24a9cf16dca 100644
--- a/src/test/modules/test_cplusplusext/meson.build
+++ b/src/test/modules/test_cplusplusext/meson.build
@@ -4,11 +4,6 @@ if not have_cxx
   subdir_done()
 endif
 
-# Currently not supported, to be fixed.
-if cc.get_id() == 'msvc'
-  subdir_done()
-endif
-
 test_cplusplusext_sources = files(
   'test_cplusplusext.cpp',
 )
diff --git a/src/test/modules/test_cplusplusext/sql/test_cplusplusext.sql b/src/test/modules/test_cplusplusext/sql/test_cplusplusext.sql
index a41682417ae..693910ba637 100644
--- a/src/test/modules/test_cplusplusext/sql/test_cplusplusext.sql
+++ b/src/test/modules/test_cplusplusext/sql/test_cplusplusext.sql
@@ -1,3 +1,6 @@
 CREATE EXTENSION test_cplusplusext;
 
 SELECT test_cplusplus_add(1, 2);
+
+SELECT module_name, version FROM pg_get_loaded_modules()
+  WHERE module_name = 'test_cplusplusext';
diff --git a/src/test/modules/test_cplusplusext/test_cplusplusext.cpp b/src/test/modules/test_cplusplusext/test_cplusplusext.cpp
index 435937c00d2..7108e5b1cc5 100644
--- a/src/test/modules/test_cplusplusext/test_cplusplusext.cpp
+++ b/src/test/modules/test_cplusplusext/test_cplusplusext.cpp
@@ -18,7 +18,7 @@ extern "C" {
 #include "postgres.h"
 #include "fmgr.h"
 
-PG_MODULE_MAGIC;
+PG_MODULE_MAGIC_EXT("test_cplusplusext", "1.2");
 
 PG_FUNCTION_INFO_V1(test_cplusplus_add);
 }

base-commit: 9b9eaf08ab2dc22c691b22e59f1574e0f1bcc822
-- 
2.52.0

