0

I'm familiar with the following way of creating a macro with variable number of arguments. However, consider:

#define MY_MACRO_N(value, format, ...) my_func(value, format, ##__VA_ARGS__)
#define MY_MACRO_0(value) my_func(value, NULL)

Where my_func takes variable number of arguments as well. How can I create a MY_MACRO macro that encapsulates both, such that:

MY_MACRO(my_value);  // expand to my_func(my_value, NULL);
MY_MACRO(my_value, my_format);  // expand to my_func(my_value, my_format);
MY_MACRO(my_value, my_format, a, b);  // expand to my_func(my_value, my_format, a, b);
MY_MACRO();  // invalid

The variable number of arguments break the GET_MACRO approach, however it seems like there should be a way to do this.

galah92
  • 3,621
  • 2
  • 29
  • 55
  • Do you want your macro to accept an arbitrary number of arguments, or is there some fixed upper bound on argument count? – John Bollinger Apr 19 '20 at 14:45
  • No upper bound, as `my_func` has no upper bound. – galah92 Apr 19 '20 at 14:47
  • Then I think you're stuck. To the best of my knowledge, all potential approaches to problems of this kind have a built-in maximum on the number of arguments they can handle. You can make that maximum large (typically via large preprocessor code), but you cannot avoid it altogether. – John Bollinger Apr 19 '20 at 14:53
  • I was afraid of that. It looks promising because I only really care about the second argument - the rest is going `my_func` as is. – galah92 Apr 19 '20 at 14:55
  • 1
    If this is a logging macro with `printf`-like syntax (and it looks like it) and if you are willing to accept an empty string as default instead of `NULL` and if you are okay with restricting the format of literal strings if it is given, you can make use of string-literal concatenation. In this special, but common case, the macro looks like `#define LOG(CODE, ...) my_func(CODE, "" __VA_ARGS__)`. (But the format checker will complain about empty format strings, so that may not be a good solution.) – M Oehm Apr 19 '20 at 15:10
  • @MOehm I can't seem to understand how that helps, seems like your solution rely on passing a pre-formatted string instead of `format` and additional arguments. I need something stronger then that. – galah92 Apr 19 '20 at 15:14
  • 1
    Well, `LOG(X)` expands to `my_func(X, "")`, anything else to `my_func(X, "" "", ...)`, which will paste the empty string to your format string, [see here](https://ideone.com/VWs3Mc). That will only work if your format string is a literal, but that's usually the case. Never mind. It was just a suggestion that you might have found useful. – M Oehm Apr 19 '20 at 15:22
  • @MOehm Got it, and it actually might work for my scenario. Thanks! – galah92 Apr 19 '20 at 15:42

1 Answers1

0

When you overload a macro on number of arguments, be it using an argument list or EVAL(..) recursive expansion, there is always a maximum number of arguments it can handle. You can only set up an unrealistic limit - 64? 256? 1000? - and generate the overloads for that many macro arguments.

#define MY_MACRO_0()                    MY_MACRO_1(will do an error
#define MY_MACRO_1(value)               my_func(value, NULL)
#define MY_MACRO_V(value, format, ...)  my_func(value, format, ##__VA_ARGS__)
#define MY_MACRO_N(_4,_3,_2,_1,_0,N,...)  MY_MACRO_##N
#define MY_MACRO(...)  MY_MACRO_N(0,##__VA_ARGS__,V,V,V,1,0)(__VA_ARGS__)
MY_MACRO(my_value);  // expand to my_func(my_value, NULL);
MY_MACRO(my_value, my_format);  // expand to my_func(my_value, my_format);
MY_MACRO(my_value, my_format, a, b);  // expand to my_func(my_value, my_format, a, b);
MY_MACRO();  // invalid

Still, as noted in the commands, for logging functions it's typical to pass an empty string literal in case of missing format string, then just check if (*fmt == '\0') in the logging function.

#define MY_MACRO(value, ...)   my_func(value, "" __VA_ARGS__)
KamilCuk
  • 120,984
  • 8
  • 59
  • 111