I've been aware of the VA_NARGS
macro as described at C Preprocessor, Macro "Overloading"
for a while, but I've always been put off by the large amount of boilerplate that it takes to make it work.
I recently had a need for this functionality, and so I gritted my teeth and wrote all the requisite macro code, in all its "glory."
In my particular case, I can consistently rely on all of the varargs arguments to be of a particular type. This led me to think that perhaps there's a better way, using sizeof
and an array type. I tried this out on my local system, and it seems to work. However, I am concerned that this solution may be brittle (beyond the type restriction).
My questions are: Is this in fact a safe and reasonably sane solution to the problem? Or perhaps: What trouble am I asking for, down the line, if I use this? And finally: To the extent that there are problems with the attempt (below), are there tweaks that can be applied in order to salvage the general approach?
Here's the code, along with a demo main()
function. In this case, the varargs arguments all have to be ints:
#include <stdio.h>
#define ARG_ARRAY(...) ((int[]) { __VA_ARGS__ })
#define ARG_COUNT(...) (sizeof (ARG_ARRAY(__VA_ARGS__)) / sizeof (int))
#define STUFF(label, ...) \
stuff(label, ARG_COUNT(__VA_ARGS__), ARG_ARRAY(__VA_ARGS__))
void stuff(char *label, int count, int *values) {
printf("[%s] count %d", label, count);
for (int i = 0; i < count; i++) {
printf("%s %d", (i == 0) ? ":" : ",", values[i]);
}
printf("\n");
}
int return1(void) {
printf("Called `return1()`.\n");
return 1;
}
int main(int argc, char **argv) {
STUFF("blort");
STUFF("frotz", return1());
STUFF("fizmo", 2 + 3, 6 + 1);
STUFF("glorf", 99, 999, 999);
STUFF("igram", 9, 8, 7, 6, 5, 4, 3, 2, 1);
}
Here's a transcript:
[blort] count 0
Called `return1()`.
[frotz] count 1: 1
[fizmo] count 2: 5, 7
[glorf] count 3: 99, 999, 999
[igram] count 9: 9, 8, 7, 6, 5, 4, 3, 2, 1
The return1()
printout is to verify that the function isn't called twice.
UPDATES:
It was pointed out in the comments that (int[]) { args }
is C99 but not C++. In my case I can count on using a C compiler for the code in question, but it's still good to know this particular limitation.
It was pointed out in a now-deleted answer that C99 requires a varargs macro argument to be filled by at least one actual argument (though I think the spec is at best ambiguous in that regard). The compiler I have at hand (Clang on OS X) accepts the code with -Wall
but does in fact complain with -pedantic
, as @2501 aptly demonstrates in their comment.
-pedantic
also complains about a zero size array (the no-args expansion (int[]) { }
), though this could be fixed by always including a dummy element.