Support __VA_OPT__

Fixes #1574
diff --git a/Doc/Manual/CPlusPlus20.html b/Doc/Manual/CPlusPlus20.html
index 98cb286..023de23 100644
--- a/Doc/Manual/CPlusPlus20.html
+++ b/Doc/Manual/CPlusPlus20.html
@@ -18,6 +18,10 @@
 <li><a href="#CPlusPlus20_lambda_templates">Lambda templates</a>
 <li><a href="#CPlusPlus20_constexpr_destructors">Constexpr destructors</a>
 </ul>
+<li><a href="#CPlusPlus20_preprocessor_changes">Preprocessor changes</a>
+<ul>
+<li><a href="#CPlusPlus20_va_opt">__VA_OPT__()</a>
+</ul>
 <li><a href="#CPlusPlus20_standard_library_changes">Standard library changes</a>
 </ul>
 </div>
@@ -84,7 +88,18 @@
 </pre>
 </div>
 
-<H2><a name="CPlusPlus20_standard_library_changes">10.3 Standard library changes</a></H2>
+<H2><a name="CPlusPlus20_preprocessor_changes">10.3 Preprocessor changes</a></H2>
+
+
+<H3><a name="CPlusPlus20_va_opt">10.3.1 __VA_OPT__()</a></H3>
+
+
+<p>
+Support for <tt>__VA_OPT__()</tt> was added in SWIG 4.3.0.
+</p>
+
+
+<H2><a name="CPlusPlus20_standard_library_changes">10.4 Standard library changes</a></H2>
 
 
 </body>
diff --git a/Doc/Manual/Preprocessor.html b/Doc/Manual/Preprocessor.html
index e4d4425..1b93f6e 100644
--- a/Doc/Manual/Preprocessor.html
+++ b/Doc/Manual/Preprocessor.html
@@ -16,7 +16,7 @@
 <li><a href="#Preprocessor_condition_compilation">Conditional Compilation</a>
 <li><a href="#Preprocessor_nn5">Macro Expansion</a>
 <li><a href="#Preprocessor_nn6">SWIG Macros</a>
-<li><a href="#Preprocessor_nn7">C99 and GNU Extensions</a>
+<li><a href="#Preprocessor_nn7">Variadic Macros</a>
 <li><a href="#Preprocessor_delimiters">Preprocessing and delimiters</a>
 <ul>
 <li><a href="#Preprocessor_nn8">Preprocessing and %{ ... %} &amp; " ... " delimiters</a>
@@ -322,11 +322,12 @@
 support).
 </p>
 
-<H2><a name="Preprocessor_nn7">11.6 C99 and GNU Extensions</a></H2>
+<H2><a name="Preprocessor_nn7">11.6 Variadic Macros</a></H2>
 
 
 <p>
-SWIG-1.3.12 and newer releases support variadic preprocessor macros.  For example:
+SWIG-1.3.12 and newer releases support variadic preprocessor macros which were
+standardised by C99 and C++11.  For example:
 </p>
 
 <div class="code">
@@ -342,8 +343,9 @@
 </p>
 
 <p>
-SWIG allows a variable number of arguments to be empty.  However, this often results
-in an extra comma (, ) and syntax error in the resulting expansion. For example:
+The variable arguments can be empty.  However, this often results
+in an extra comma (<tt>,</tt>) and syntax error in the resulting expansion. For
+example:
 </p>
 
 <div class="code">
@@ -353,7 +355,25 @@
 </div>
 
 <p>
-To get rid of the extra comma, use <tt>##</tt> like this:
+C++20 and C23 added <tt>__VA_OPT__()</tt> as a solution to this, which SWIG
+4.3.0 added support for.  <tt>__VA_OPT__()</tt> expands to its argument if the
+variable arguments contain any tokens, and to nothing otherwise.  It can be
+used to solve the problem above like so:
+</p>
+
+<div class="code">
+<pre>
+#define DEBUGF(fmt, ...)   fprintf(stderr, fmt __VA_OPT__(,) __VA_ARGS__)
+</pre>
+</div>
+
+<p>
+An early non-standardised solution to this problem which gave a special
+meaning to the token sequence <tt>, ## __VAR_ARGS__</tt> is supported by
+several C and C++ compilers, and also by SWIG 4.3.0 and later (it was
+documented as supported by earlier SWIG versions, but didn't actually work in
+at least SWIG 2.x and 3.x).  Using this feature you can get rid of the extra
+comma like this:
 </p>
 
 <div class="code">
@@ -363,7 +383,8 @@
 </div>
 
 <p>
-SWIG also supports GNU-style variadic macros.    For example:
+SWIG also supports GNU-style variadic macros, which specify a name for the
+variable arguments instead of using <tt>__VA_ARGS__<tt>.  For example:
 </p>
 
 <div class="code">
@@ -373,11 +394,12 @@
 </div>
 
 <p>
-<b>Comment:</b> It's not entirely clear how variadic macros might be useful to
-interface building.   However, they are used internally to implement a number of
-SWIG directives and are provided to make SWIG more compatible with C99 code.
+SWIG supports <tt>__VA_OPT__()</tt> in combination with GNU-style variadic
+macros (following the lead of GCC and clang which also support this, albeit
+with a warning by default).
 </p>
 
+
 <H2><a name="Preprocessor_delimiters">11.7 Preprocessing and delimiters</a></H2>
 
 
diff --git a/Examples/test-suite/php/preproc_runme.php b/Examples/test-suite/php/preproc_runme.php
index 0c91c19..cf8eb04 100644
--- a/Examples/test-suite/php/preproc_runme.php
+++ b/Examples/test-suite/php/preproc_runme.php
@@ -7,7 +7,7 @@
 // New classes
 check::classes(array('preproc','EmbeddedDefines','TypeNameTraits','tcxMessageTest','tcxMessageBug'));
 // No new vars
-check::globals(array('endif_','define','defined','FOO','BAR','global_var','global_var2'));
+check::globals(array('endif_','define','defined','FOO','BAR','global_var','global_var2','global_var3','global_var4','global_var5','global_var6','global_var7','global_var8','global_var9','global_var10','global_var11','global_var12'));
 
 check::equal(preproc::endif__get(), 1);
 check::equal(preproc::define_get(), 1);
diff --git a/Examples/test-suite/preproc.i b/Examples/test-suite/preproc.i
index 864a159..4d2d5c6 100644
--- a/Examples/test-suite/preproc.i
+++ b/Examples/test-suite/preproc.i
@@ -472,3 +472,41 @@
 int global_var = 42;
 int global_var2 = 345;
 %}
+
+/* Feature test for __VA_OPT__ support.  This was standardised in C++20 but
+ * we're only testing SWIG's support for it here so this doesn't require a
+ * C++20 compiler.
+ */
+#define DECLARE_GLOBAL_VA(N,...) int global_var##N __VA_OPT__(=)__VA_ARGS__;
+/* Named varargs is a GCC extension.  Both GCC and clang support __VA_OPT__
+ * with named varargs (albeit with a warning) and SWIG supports it too for
+ * compatibility.
+ */
+#define DECLARE_GLOBAL_NAMED(N,NAMED...) int global_var##N __VA_OPT__(=)NAMED;
+DECLARE_GLOBAL_VA(3)
+DECLARE_GLOBAL_VA(4,)
+DECLARE_GLOBAL_VA(5, )
+DECLARE_GLOBAL_VA(6, /**Hello,World)**/ )
+
+DECLARE_GLOBAL_NAMED(7)
+DECLARE_GLOBAL_NAMED(8,)
+DECLARE_GLOBAL_NAMED(9, )
+DECLARE_GLOBAL_NAMED(10, /**Hello,World)**/ )
+
+DECLARE_GLOBAL_VA(11,111)
+
+DECLARE_GLOBAL_NAMED(12,121)
+
+// Show the compiler simple definitions so we don't need __VA_OPT__ support.
+%{
+int global_var3 = 3;
+int global_var4 = 4;
+int global_var5 = 5;
+int global_var6 = 6;
+int global_var7 = 7;
+int global_var8 = 8;
+int global_var9 = 9;
+int global_var10 = 10;
+int global_var11 = 111;
+int global_var12 = 121;
+%}
diff --git a/Source/Preprocessor/cpp.c b/Source/Preprocessor/cpp.c
index b4ab74b..c8d0e32 100644
--- a/Source/Preprocessor/cpp.c
+++ b/Source/Preprocessor/cpp.c
@@ -503,6 +503,10 @@
   Replace(macrovalue, "\001@", "\004", DOH_REPLACE_ANY);
   /* Replace '##@' with a special token */
   Replace(macrovalue, "\002@", "\005", DOH_REPLACE_ANY);
+  if (varargs) {
+    /* Replace '__VA_OPT__' with a special token */
+    Replace(macrovalue, "__VA_OPT__", "\006", DOH_REPLACE_ID|DOH_REPLACE_ANY);
+  }
 
   /* Go create the macro */
   macro = NewHash();
@@ -918,6 +922,38 @@
 	Printf(tempa, "\"%s\"", arg);
 	Replace(ns, temp, tempa, DOH_REPLACE_ID_END);
       }
+      if (isvarargs && i == l - 1) {
+	char *s = Char(ns);
+	char *a = s;
+	while ((a = strchr(a, '\006')) != NULL) {
+	  *a = ' ';
+	  while (isspace((unsigned char)*++a)) { }
+	  if (*a == '(') {
+	    char *e = a;
+	    int depth = 1;
+	    while (*++e) {
+	      if (*e == ')') {
+		if (--depth == 0) break;
+	      } else if (*e == '(') {
+		++depth;
+	      }
+	    }
+	    if (*e) {
+	      if (Len(arg) == 0) {
+		// Empty varargs so replace ( and ) and everything between with
+		// spaces.
+		memset(a, ' ', e - a + 1);
+	      } else {
+		// Non-empty varargs so replace ( and ) with spaces.
+		*a = ' ';
+		*e = ' ';
+	      }
+	    }
+	    a = e + 1;
+	  }
+	}
+      }
+
       if (strchr(Char(ns), '\002')) {
 	/* Look for concatenation tokens */
 	Clear(temp);