I need to write a variadic macro in C which must take zero or more arguments.
In gcc, that can be achieved by adding \"##\" after the comma, e.g. ,##____VA_ARG
Using a simplified version what Jens spelled out, a possible approach is described below.
E_
is used to inject an argument, while I_
is used to cause indirect invocation of a macro. V_
is required to add an additional level of indirection, which seems required for some compilers.
#define V_(...) __VA_ARGS__
#define I_(M,...) V_(M(__VA_ARGS__))
#define E_() _,
Now, suppose you need a macro MAC
to handle 0, 1, or 2 arguments:
MAC(1, 2);
MAC(1);
MAC();
One possible implementation might look like:
#define MAC_X(_2,_1,X,...) MAC_##X
#define MAC(...) V_(V_(MAC_X(__VA_ARGS__,2,_))(__VA_ARGS__))
#define MAC_2(...) function_for_2(__VA_ARGS__)
#define MAC_1(...) function_for_1(__VA_ARGS__)
#define MAC_0(...) function_for_0(__VA_ARGS__)
#define MAC_Y(_1,Y,...) MAC_##Y
#define MAC__(...) I_(MAC_Y, E_ __VA_ARGS__ () 0, 1)(__VA_ARGS__)
And expanding it to 3 arguments is relatively straightforward. The only messiness is detecting 1 or 0 arguments, which does not change:
-#define MAC_X(_2,_1,X,...) MAC_##X
-#define MAC(...) V_(V_(MAC_X(__VA_ARGS__,2,_))(__VA_ARGS__))
+#define MAC_X(_3,_2,_1,X,...) MAC_##X
+#define MAC(...) V_(V_(MAC_X(__VA_ARGS__,3,2,_))(__VA_ARGS__))
+#define MAC_3(...) function_for_3(__VA_ARGS__)
#define MAC_2(...) function_for_2(__VA_ARGS__)
For more than 1 argument, the MAC
macro expands into MAC_2
(or MAC_3
). But, for 1 or 0 arguments, the MAC
macro expands to MAC__
.
The MAC__
macro applies Jens' trick to detect whether it was passed 1 or 0 arguments. It does this with the E_
helper macro, and injecting its argument between E_
and the ()
that would cause it to be invoked. If there are 0 arguments, the E_
is invoked, and an argument is injected. The injected argument causes 0
to be the second argument for MAC_Y
to select. If there is 1 argument, E_
is not expanded. The first argument to the to MAC_Y
becomes E_ ... () 0
(where the ...
is whatever the first argument was), and the second argument for MAC_Y
is 1
. This allows MAC_Y
to invoke either MAC_0
or MAC_1
.