I am trying to write a C variadic macro that checks if it was invoked with one, or many arguments.
I found some solutions which use a macro that counts the arguments, but those solutions are either for a fixed/finite amount of arguments, or rely on the arguments sharing the same type:
#define VA_NARGS_IMPL(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N
#define VA_NARGS(...) VA_NARGS_IMPL(__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
Counts arguments upto 10, then fails. However I think that checking 1 vs many arguments is an easier problem, so it might have a solution that will work with any type and for any number of arguments.
My use for this is in a sort of debug_log
macro:
#define debug_log(...) \
do { \
fprintf(verbose, " - %lu ", time(NULL)); \
if (VA_HAS_MANY_ARGS(__VA_ARGS__)) { \
fprintf(verbose, __VA_ARGS__); \
} else {
fprintf(verbose, "%s\n", __VA_ARGS__); \
} \
while (0)
Which can be invoked as debug_log("string here");
but also debug_log("string there %i\n", 5);
and will produce:
- current_time_here string here\n
- current_time_there string there 5\n
respectively.
Here's how you might code a one-or-many macro:
#define SECOND(...) SECOND_I(__VA_ARGS__,,)
#define SECOND_I(A,B,...) B
#define SHIFT_IN_ZERO , 0
#define ONE_OR_MANY_UTILITY(...) ONE_OR_MANY_UTILITY_I(__VA_ARGS__, SHIFT_IN_ZERO, X)
#define ONE_OR_MANY_UTILITY_I(A,B,...) B
#define ONE_OR_MANY(...) SECOND(ONE_OR_MANY_UTILITY(__VA_ARGS__), 1)
However, note that this is a fairly complex macro system (more complex than VA_NARGS
), and I'm not sure you're gaining from this in the use case in your question (which I consider part of the question).
What you are in essence trying to accomplish seems misguided to me. You want to collapse various usages down to a single macro, debug_log
; this itself is a type of polymorphism. That's a gain. However, you're specifically trying to make one of these cases (calling debug_log with one parameter) behave differently (add a NL for you) from the other cases (calling debug_log with more than one parameter)... and this negates any benefit you gain.
If you spell the macro the same way, it should be doing the same thing. Benefiting me by allowing me to only have to remember/use(/transform) one macro, but then forcing me to learn(/change) how to use it based on how many arguments I pass it, is just teasing me. Either debug_log
should always add a NL for me, or it should never add one.
It would be better to use two different macros, since these do two different things. However, it would be even better to collapse these macros down into a single macro that always does the same kind of thing.
In this case, forcing the NL in is probably a good idea; that helps ensure that your log file itself is usable. But instead of adding the NL when you call it with one parameter, but not when called with multiple parameters, why not just always add the NL?:
#define debug_log(...) \
do { \
/* fprintf metadata--timestamp, file, log level, etc here */ \
fprintf(verbose, __VA_ARGS__); \
fputs("\n", verbose); \
} \
while (0)