I know I can do this:
#define MACRO(api, ...) \
bool ret = api(123, ##__VA_ARGS__);
This is just an example, it's part of a more complicated solution. The point is that I need to append the variable number of arguments to the first 123. The ## makes the compiler strip out the comma after the 123 argument if no arguments were passed into MACRO.
But now I want to append arguments to api, like so:
#define MACRO(api, ...) \
bool ret = api(__VA_ARGS__##, 456);
Nocando. One solution is to have two macros, MACRO and MACRO_V, say, and make the _V version not process any arguments. But is there a way to make it work with one macro?
Yes, you can. The following supports up to 4 arguments, but it can be trivially expanded to support more:
#define MACRO(api, ...) \
bool ret = api(__VA_ARGS__ VA_COMMA(__VA_ARGS__) 456)
/*
* VA_COMMA() expands to nothing if given no arguments and a comma if
* given 1 to 4 arguments. Bad things happen if given more than 4
* arguments. Don't do it.
*/
#define VA_COMMA(...) GET_6TH_ARG(,##__VA_ARGS__,COMMA,COMMA,COMMA,COMMA,)
#define GET_6TH_ARG(a1,a2,a3,a4,a5,a6,...) a6
#define COMMA ,
/* EXAMPLES */
MACRO(foo) /* bool ret = foo( 456) */
MACRO(foo,1) /* bool ret = foo(1 , 456) */
MACRO(foo,1,2,3,4) /* bool ret = foo(1,2,3,4 , 456) */
/* uh oh, too many arguments: */
MACRO(foo,1,2,3,4,5) /* bool ret = foo(1,2,3,4,5 5 456) */
MACRO(foo,1,2,3,4,5,6) /* bool ret = foo(1,2,3,4,5,6 5 456) */
This same trick is used to:
VA_COMMA
surrounds its arguments (__VA_ARGS__
) with six additional arguments: one empty argument before (doesn't have to be empty—it's thrown away) and four commas and an empty argument after.
These six or more arguments are passed to GET_6TH_ARG
, which, as its name implies, expands to the sixth argument. All other arguments are discarded.
Thus, MACRO(foo)
is expanded like this:
step 0: MACRO(foo)
step 1: bool ret = foo( VA_COMMA() 456)
step 2: bool ret = foo( GET_6TH_ARG(,COMMA,COMMA,COMMA,COMMA,) 456)
step 3: bool ret = foo( 456)
MACRO(foo,1)
is expanded like this:
step 0: MACRO(foo,1)
step 1: bool ret = foo(1 VA_COMMA(1) 456)
step 2: bool ret = foo(1 GET_6TH_ARG(,1,COMMA,COMMA,COMMA,COMMA,) 456)
step 3: bool ret = foo(1 COMMA 456)
step 4: bool ret = foo(1 , 456)
MACRO(foo,1,2)
is expanded like this:
step 0: MACRO(foo,1,2)
step 1: bool ret = foo(1,2 VA_COMMA(1,2) 456)
step 2: bool ret = foo(1,2 GET_6TH_ARG(,1,2,COMMA,COMMA,COMMA,COMMA,) 456)
step 3: bool ret = foo(1,2 COMMA 456)
step 4: bool ret = foo(1,2 , 456)
MACRO(foo,1,2,3,4,5)
is expanded like this:
step 0: MACRO(foo,1,2,3,4,5)
step 1: bool ret = foo(1,2,3,4,5 VA_COMMA(1,2,3,4,5) 456)
step 2: bool ret = foo(1,2,3,4,5 GET_6TH_ARG(,1,2,3,4,5,COMMA,COMMA,COMMA,COMMA,) 456)
step 3: bool ret = foo(1,2,3,4,5 5 456)