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 %{ ... %} & " ... " 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);