Counting variadic arguments

The following code is simplified version of a code snippet which I’ve found on stack overflow site.

#define VA_COUNT(…) VA_COUNT0(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1)
#define VA_COUNT0(_1, _2, _3, _4, _5, _6, _7, _8, _9, N, …) N

Being able to count at preprocessing stage, means many things.
There’re a bunch of subtle, dirty expressions related to variadic macro or function in C.

A direct example is to pass the number of arguments to a variadic function.

void f_(int n,);
#define f(…) f_(VA_COUNT(__VA_ARGS__), __VA_ARGS__)

@ It seems that boost has similar functionality, but personally I avoid boost because it has too many “interdependencies”… Don’t know how boost does it.

Defining VA_MAP as below, enables a similar effect to map() of python or perl.

/* concatenate after macro expansion */
#define CONCAT(x, y) CONCAT0(x, y)
#define CONCAT0(x, y) x##y

#define VA_MAP(f, …)  CONCAT(VA_MAP_, VA_COUNT(__VA_ARGS__))(f, __VA_ARGS__)
#define VA_MAP_9(f, x, …)     f(x), VA_MAP_8(f, __VA_ARGS__)
#define VA_MAP_8(f, x, …)     f(x), VA_MAP_7(f, __VA_ARGS__)
#define VA_MAP_7(f, x, …)     f(x), VA_MAP_6(f, __VA_ARGS__)
#define VA_MAP_6(f, x, …)     f(x), VA_MAP_5(f, __VA_ARGS__)
#define VA_MAP_5(f, x, …)     f(x), VA_MAP_4(f, __VA_ARGS__)
#define VA_MAP_4(f, x, …)     f(x), VA_MAP_3(f, __VA_ARGS__)
#define VA_MAP_3(f, x, …)     f(x), VA_MAP_2(f, __VA_ARGS__)
#define VA_MAP_2(f, x, …)     f(x), VA_MAP_1(f, __VA_ARGS__)
#define VA_MAP_1(f, x)          f(x)

A sample example is to pass the size of an argument along with the argument itself.

void f_(int size,)
#define WITH_SIZE(x)  sizeof(x), x
#define f(…) f_(VA_MAP(WITH_SIZE, __VA_ARGS__), 0)

Note that this example is not that usable, because string literal is passed as a character pointer, but sizeof() of string literal returns the length of string, not sizeof(const char *)

Here’s another bonus tip. Define a macro which selects a first argument of variadic macro, then it can be used to avoid dangling comma problem without using ,## extension of gcc.

#define VA_FIRST(…)           VA_FIRST0(__VA_ARGS__, dummy)
#define VA_FIRST0(arg, …)     arg

For example,

void fail(const char *file, const char *line, const char *fmt,);
#define check(cond, …) \
    do { if (!cond) fail(__FILE__, __LINE__, ##__VA_ARGS__); } while (0)

Instead, the following code is more portable, although it needs fail() to have an extra dummy argument.

void fail(const char *file, const char *line, int dummy, const char *fmt,);
#define check(/* cond, */ …)  \
    do { if (!VA_FIRST(__VA_ARGS__)) fail(__FILE__, __LINE__, __VA_ARGS__); } while (0)