0

I'm trying to create some trace macros that use variadic arguments. The macros only seem to function correctly when a named argument precedes the variadic ones.

A minimal code version of what I'm doing is shown below. Only TraceTest() without arguments fails. I have also tried to create an intermediate macro that passes a dummy first argument to TraceTest1, but that fails too.

template<typename ...Args>
inline void f(const char*, Args&&... args) { }

#define TraceTest1(a, args...) f("Trace Start ", ##args)
#define TraceTest(args...) f("Trace Start", ##args)

TraceTest();     // error: expected primary-expression before ‘)’ token
TraceTest("a");  // works
TraceTest1();    // works
TraceTest1("a"); // works

I have read the gnu docs on variadic macros but could find nothing that would explain this.

I am using gcc 7.4.0 under Ubuntu 18.04 and compiling with

g++ -Wall -Wextra -std=c++17 src/event.cpp -obin/event
M. Webb
  • 157
  • 11

1 Answers1

2

TraceTest() expands to f("Trace start",), which is obviously a syntax error.

Both named variadic macro parameters and token pasting of ',' and variadic macro parameters are not standard C++ (even though every compiler I can find implements the latter). If you want them with GCC, use "-std=gnu++17" instead of "-std=c++17".


Note that C++20 is adding a new __VA_OPT__ preprocessor token that can be used to do this in a portable way:

#define TraceTest(...) f("Trace Start " __VA_OPT__(,) __VA_ARGS__)

Live Demo

__VA_OPT__ is replaced with its parameters only if __VA_ARGS__ isn't empty, so TraceTest() will be replaced with f("Trace Start ").

Miles Budnek
  • 28,216
  • 2
  • 35
  • 52
  • Probably worth mentioning that OP can use the GNU extension of `#define TraceTest(...) f("Trace Start ",##__VA_ARGS__)` where the extension is that `,##__VA_ARGS__` expands to blank (consuming the comma) if there were no variadic arguments. I wonder why they didn't just standardize that extension. – M.M May 30 '19 at 03:01
  • 1
    @M.M `__VA_OPT__` is a bit more powerful. The example from the standard is `#define SDEF(sname, ...) S sname __VA_OPT__(= { __VA_ARGS__ })`, so `SDEF(foo);` expands to `S foo;` and `SDEF(foo, 1, 2)` expands to `S foo = {1, 2};`. – Miles Budnek May 30 '19 at 03:06
  • @M.M Also, token pasting with "," and `__VA_ARGS__` is what OP is already using, and I mentioned that they can keep using that with "-std=gnu++17". – Miles Budnek May 30 '19 at 03:09
  • They were using `,##args`, not `,##__VA_ARGS__`. But it turns out that my suggested version gives the same error . (I use the form with one fixed argument in my own codebase with `-std=c++17` and it works fine, I incorrectly assumed it would behave the same with no fixed arguments) – M.M May 30 '19 at 03:23
  • This did it. Simply compiling with -gnu++17 did the trick. Out of interest, does anyone know a way to achieve the same result using standard c++? – M. Webb May 30 '19 at 04:02
  • @M.Webb I'm not aware of any. The standard says that the number of arguments given to a variadic macro must be strictly greater than the number of named parameters. I don't know of any compiler that supports variadic macros and doesn't support the `,##__VA_ARGS__` extension though. – Miles Budnek May 30 '19 at 04:15