7

You can define a variadic macro in C++ like:

#define FOO(x, ...) bar(x, __VA_ARGS__)

But calling FOO as FOO(1) results in the macro expansion bar(1,) which is obviously a syntactical error and won't compile.

Therefore GCC includes a GNU extension:

#define FOO(x, ...) bar(x, ##__VA_ARGS__)

which would expand the given example to the desired result bar(1). Although __VA_ARGS__ is a GNU extension it's support by clang too, but which emits a warning under the -pedantic flag:

warning: token pasting of ',' and __VA_ARGS__ is a GNU extension [-Wgnu-zero-variadic-macro-arguments].

Therefore C++20 includes a new mechanism to achieve the desired result in a standard compliant way:

#define FOO(x, ...) bar(x __VA_OPT__(,) __VA_ARGS__)

This will add the , only if the following __VA_ARGS__ are not empty, otherwise it will omit the ,. This new extension currently works with the GCC and clang trunks (with the -std=c++2a flag enabled): https://godbolt.org/z/k2nAE6.

My only problem is that clang emits a warning under -pedantic:

warning: must specify at least one argument for '...' parameter of variadic macro [-Wgnu-zero-variadic-macro-arguments] (GCC does not emit a warning).

But why? This only seems to make sense if someone only uses __VA_ARGS__ and passes no arguments to the macro. But with the new extension __VA_OPT__ I explicitly handle the case for which no argument is given.

So why would clang emit a warning in this case and how can I work around it?

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
Richard
  • 56,349
  • 34
  • 180
  • 251
  • 3
    Ugh, why not just standardize the extension that all major compilers already implement ??? Anyway it seems your question is just asking about a compiler defect in implementation of a new feature, so it would be more appropriate to file under clang's bug tracking system rather than here . – M.M Jun 09 '21 at 22:25
  • 1
    Is there a reason `FOO()` needs to be a variadic *macro* at all, rather than a variadic *template function*? – Remy Lebeau Jun 09 '21 at 22:34
  • @RemyLebeau: It'd be nice, but outside this example I'm relying on things like `__FILE__` and `__func__` and `__LINE__` to make nice error messages and such. – Richard Jun 10 '21 at 00:12
  • 1
    If you're using C++20 anyway, there is a now [``](https://en.cppreference.com/w/cpp/utility/source_location) so you can mostly get rid of those kinds of macros. – NathanOliver Jun 10 '21 at 01:08

1 Answers1

2

This is already legal in C++20; it appears that Clang just hasn't updated their warnings yet.

The C++20 standard (from N4868) says in [cpp.replace.general]/5:

If the identifier-list in the macro definition does not end with an ellipsis [...] Otherwise, there shall be at least as many arguments in the invocation as there are parameters in the macro definition (excluding the ...). There shall exist a ) preprocessing token that terminates the invocation.

Compare with the bolded section with the equivalent statement in C++17's [cpp.replace]/4 (from N4659):

If the identifier-list in the macro definition does not end with an ellipsis [...] Otherwise, there shall be more arguments in the invocation than there are parameters in the macro definition (excluding the ...). There shall exist a ) preprocessing token that terminates the invocation.

There is a similar comparison to be made between C++20's [cpp.replace.general]/15 and C++17's [cpp.replace]/12.

That is, C++17 had a requirement for FOO(x, ...) to be passed at least two arguments; C++20 has weakened that to only require one. Clang's -pedantic doesn't seem to have caught up yet.

N. Shead
  • 3,828
  • 1
  • 16
  • 22