Another possibility, which does not use sizeof
nor a GCC extension is to add the following to your code
#define PP_COMMASEQ_N() \
1, 1, 1, 1, \
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, \
1, 1, 1, 1, 1, 1, 1, 1, 0, 0
#define PP_COMMA(...) ,
#define PP_HASCOMMA(...) \
PP_NARG_(__VA_ARGS__, PP_COMMASEQ_N())
#define PP_NARG(...) \
PP_NARG_HELPER1( \
PP_HASCOMMA(__VA_ARGS__), \
PP_HASCOMMA(PP_COMMA __VA_ARGS__ ()), \
PP_NARG_(__VA_ARGS__, PP_RSEQ_N()))
#define PP_NARG_HELPER1(a, b, N) PP_NARG_HELPER2(a, b, N)
#define PP_NARG_HELPER2(a, b, N) PP_NARG_HELPER3_ ## a ## b(N)
#define PP_NARG_HELPER3_01(N) 0
#define PP_NARG_HELPER3_00(N) 1
#define PP_NARG_HELPER3_11(N) N
The result is
PP_NARG() // expands to 0
PP_NARG(x) // expands to 1
PP_NARG(x, 2) // expands to 2
Explanation:
The trick in these macros is that PP_HASCOMMA(...)
expands to 0 when called with zero or one argument and to 1 when called with at least two arguments. To distinguish between these two cases, I used PP_COMMA __VA_ARGS__ ()
, which returns a comma when __VA_ARGS__
is empty and returns nothing when __VA_ARGS__
is non-empty.
Now there are three possible cases:
-
__VA_ARGS__
is empty:PP_HASCOMMA(__VA_ARGS__)
returns 0 andPP_HASCOMMA(PP_COMMA __VA_ARGS__ ())
returns 1. -
__VA_ARGS__
contains one argument:PP_HASCOMMA(__VA_ARGS__)
returns 0 andPP_HASCOMMA(PP_COMMA __VA_ARGS__ ())
returns 0. -
__VA_ARGS__
contains two or more arguments:PP_HASCOMMA(__VA_ARGS__)
returns 1 andPP_HASCOMMA(PP_COMMA __VA_ARGS__ ())
returns 1.
The PP_NARG_HELPERx
macros are just needed to resolve these cases.
Edit:
In order to fix the func(0, )
problem, we need to test whether we have supplied zero
or more arguments. The PP_ISZERO
macro comes into play here.
#define PP_ISZERO(x) PP_HASCOMMA(PP_ISZERO_HELPER_ ## x)
#define PP_ISZERO_HELPER_0 ,
Now let’s define another macro which prepends the number of arguments to an argument list:
#define PP_PREPEND_NARG(...) \
PP_PREPEND_NARG_HELPER1(PP_NARG(__VA_ARGS__), __VA_ARGS__)
#define PP_PREPEND_NARG_HELPER1(N, ...) \
PP_PREPEND_NARG_HELPER2(PP_ISZERO(N), N, __VA_ARGS__)
#define PP_PREPEND_NARG_HELPER2(z, N, ...) \
PP_PREPEND_NARG_HELPER3(z, N, __VA_ARGS__)
#define PP_PREPEND_NARG_HELPER3(z, N, ...) \
PP_PREPEND_NARG_HELPER4_ ## z (N, __VA_ARGS__)
#define PP_PREPEND_NARG_HELPER4_1(N, ...) 0
#define PP_PREPEND_NARG_HELPER4_0(N, ...) N, __VA_ARGS__
The many helpers are again needed to expand the macros to numeric values. Finally test it:
#define my_func(...) func(PP_PREPEND_NARG(__VA_ARGS__))
my_func() // expands to func(0)
my_func(x) // expands to func(1, x)
my_func(x, y) // expands to func(2, x, y)
my_func(x, y, z) // expands to func(3, x, y, z)
Online example:
http://coliru.stacked-crooked.com/a/73b4b6d75d45a1c8
See also:
Please have also a look at the P99 project, which has much more
advanced preprocessor solutions, like these.