Overloading Macro on Number of Arguments
I have two macros FOO2
and FOO3
:
#define FOO2(x,y) ...
#define FOO3(x,y,z) ...
I want to define a new macro FOO
as follows:
#define FOO(x,y) FOO2(x,y)
#define FOO(x,y,z) FOO3(x,y,z)
But this doesn't work because macros do not overload on number of arguments.
Without modifying FOO2
and FOO3
, is there some way to define a macro FOO
(using __VA_ARGS__
or otherwise) to get the same effect of dispatching FOO(x,y)
to FOO2
, and FOO(x,y,z)
to FOO3
?
Simple as:
#define GET_MACRO(_1,_2,_3,NAME,...) NAME
#define FOO(...) GET_MACRO(__VA_ARGS__, FOO3, FOO2)(__VA_ARGS__)
So if you have these macros:
FOO(World, !) # expands to FOO2(World, !)
FOO(foo,bar,baz) # expands to FOO3(foo,bar,baz)
If you want a fourth one:
#define GET_MACRO(_1,_2,_3,_4,NAME,...) NAME
#define FOO(...) GET_MACRO(__VA_ARGS__, FOO4, FOO3, FOO2)(__VA_ARGS__)
FOO(a,b,c,d) # expeands to FOO4(a,b,c,d)
Naturally, if you define FOO2
, FOO3
and FOO4
, the output will be replaced by those of the defined macros.
To add on to netcoder's answer, you CAN in fact do this with a 0-argument macro, with the help of the GCC ##__VA_ARGS__
extension:
#define GET_MACRO(_0, _1, _2, NAME, ...) NAME
#define FOO(...) GET_MACRO(_0, ##__VA_ARGS__, FOO2, FOO1, FOO0)(__VA_ARGS__)
Here is a more general solution:
// get number of arguments with __NARG__
#define __NARG__(...) __NARG_I_(__VA_ARGS__,__RSEQ_N())
#define __NARG_I_(...) __ARG_N(__VA_ARGS__)
#define __ARG_N( \
_1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \
_11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \
_21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \
_31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \
_41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \
_51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \
_61,_62,_63,N,...) N
#define __RSEQ_N() \
63,62,61,60, \
59,58,57,56,55,54,53,52,51,50, \
49,48,47,46,45,44,43,42,41,40, \
39,38,37,36,35,34,33,32,31,30, \
29,28,27,26,25,24,23,22,21,20, \
19,18,17,16,15,14,13,12,11,10, \
9,8,7,6,5,4,3,2,1,0
// general definition for any function name
#define _VFUNC_(name, n) name##n
#define _VFUNC(name, n) _VFUNC_(name, n)
#define VFUNC(func, ...) _VFUNC(func, __NARG__(__VA_ARGS__)) (__VA_ARGS__)
// definition for FOO
#define FOO(...) VFUNC(FOO, __VA_ARGS__)
Define your functions:
#define FOO2(x, y) ((x) + (y))
#define FOO3(x, y, z) ((x) + (y) + (z))
// it also works with C functions:
int FOO4(int a, int b, int c, int d) { return a + b + c + d; }
Now you can use FOO
with 2, 3 and 4 arguments:
FOO(42, 42) // will use makro function FOO2
FOO(42, 42, 42) // will use makro function FOO3
FOO(42, 42, 42, 42) // will call FOO4 function
Limitations
- Only up to 63 arguments (but expandable)
- Function for no argument only in GCC possible
Ideas
Use it for default arguments:
#define func(...) VFUNC(func, __VA_ARGS__)
#define func2(a, b) func4(a, b, NULL, NULL)
#define func3(a, b, c) func4(a, b, c, NULL)
// real function:
int func4(int a, int b, void* c, void* d) { /* ... */ }
Use it for functions with possible infinite number of arguments:
#define SUM(...) VFUNC(SUM, __VA_ARGS__)
#define SUM2(a, b) ((a) + (b))
#define SUM3(a, b, c) ((a) + (b) + (c))
#define SUM4(a, b, c) ((a) + (b) + (c) + (d))
// ...
PS: __NARG__
is copied from Laurent Deniau & Roland Illig here: https://groups.google.com/group/comp.std.c/browse_thread/thread/77ee8c8f92e4a3fb/346fc464319b1ee5?pli=1
I was just researching this myself, and I came across this here. The author added default argument support for C functions via macros.
I'll try to briefly summarize the article. Basically, you need to define a macro that can count arguments. This macro will return 2, 1, 0, or whatever range of arguments it can support. Eg:
#define _ARG2(_0, _1, _2, ...) _2
#define NARG2(...) _ARG2(__VA_ARGS__, 2, 1, 0)
With this, you need to create another macro that takes a variable number of arguments, counts the arguments, and calls the appropriate macro. I've taken your example macro and combined it with the article's example. I have FOO1 call function a() and FOO2 call function a with argument b (obviously, I'm assuming C++ here, but you can change the macro to whatever).
#define FOO1(a) a();
#define FOO2(a,b) a(b);
#define _ARG2(_0, _1, _2, ...) _2
#define NARG2(...) _ARG2(__VA_ARGS__, 2, 1, 0)
#define _ONE_OR_TWO_ARGS_1(a) FOO1(a)
#define _ONE_OR_TWO_ARGS_2(a, b) FOO2(a,b)
#define __ONE_OR_TWO_ARGS(N, ...) _ONE_OR_TWO_ARGS_ ## N (__VA_ARGS__)
#define _ONE_OR_TWO_ARGS(N, ...) __ONE_OR_TWO_ARGS(N, __VA_ARGS__)
#define FOO(...) _ONE_OR_TWO_ARGS(NARG2(__VA_ARGS__), __VA_ARGS__)
So if you have
FOO(a)
FOO(a,b)
The preprocessor expands that to
a();
a(b);
I would definitely read the article that I linked. It's very informative and he mentions that NARG2 won't work on empty arguments. He follows this up here.