Support __VA_OPT__

Fixes #1574
This commit is contained in:
Olly Betts 2024-03-26 13:43:38 +13:00
parent d1303767e9
commit a7eca8b415
5 changed files with 123 additions and 12 deletions

View File

@ -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 @@ public:
</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>

View File

@ -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 @@ many of SWIG's advanced features and libraries are built using this mechanism (s
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 @@ macros defined using <tt>%define</tt>.
</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 @@ DEBUGF("hello"); --&gt; fprintf(stderr, "hello", );
</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 @@ To get rid of the extra comma, use <tt>##</tt> like this:
</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 @@ SWIG also supports GNU-style variadic macros. For example:
</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>

View File

@ -7,7 +7,7 @@ check::functions(array('hello0','hello1','hello2','f','test','method','methodX',
// 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);

View File

@ -472,3 +472,41 @@ DECLARE_GLOBAL_VAR2()
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;
%}

View File

@ -503,6 +503,10 @@ Hash *Preprocessor_define(const_String_or_char_ptr _str, int swigmacro) {
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 @@ static String *expand_macro(String *name, List *args, String *line_file) {
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);