3

I need to write a variadic macro in C which must take zero or more arguments.

In gcc, that can be achieved by adding "##" after the comma, e.g. ,##____VA_ARGS____ as answered in Variadic macros with zero arguments.

However, the compiler in my build system (beyond my control) does not understand the ,## syntax and so does not swallow the comma.

Is there a workaround I can use?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Gnubie
  • 2,587
  • 4
  • 25
  • 38

2 Answers2

4

Yes, gcc swallowing the comma is non standard and you should not rely on that.

With C99 conforming preprocessors you may achieve a similar effect by testing for a macro arguments that is the empty token. For the ideas of how this works you could see here, for a whole set of preprocessor macros that ease the programming of such features there is P99.

Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
1

Using a simplified version what Jens spelled out, a possible approach is described below.

E_ is used to inject an argument, while I_ is used to cause indirect invocation of a macro. V_ is required to add an additional level of indirection, which seems required for some compilers.

#define V_(...) __VA_ARGS__
#define I_(M,...) V_(M(__VA_ARGS__))
#define E_() _,

Now, suppose you need a macro MAC to handle 0, 1, or 2 arguments:

MAC(1, 2);
MAC(1);
MAC();

One possible implementation might look like:

#define MAC_X(_2,_1,X,...) MAC_##X
#define MAC(...) V_(V_(MAC_X(__VA_ARGS__,2,_))(__VA_ARGS__))

#define MAC_2(...) function_for_2(__VA_ARGS__)
#define MAC_1(...) function_for_1(__VA_ARGS__)
#define MAC_0(...) function_for_0(__VA_ARGS__)

#define MAC_Y(_1,Y,...) MAC_##Y
#define MAC__(...) I_(MAC_Y, E_ __VA_ARGS__ () 0, 1)(__VA_ARGS__)

And expanding it to 3 arguments is relatively straightforward. The only messiness is detecting 1 or 0 arguments, which does not change:

-#define MAC_X(_2,_1,X,...) MAC_##X
-#define MAC(...) V_(V_(MAC_X(__VA_ARGS__,2,_))(__VA_ARGS__))
+#define MAC_X(_3,_2,_1,X,...) MAC_##X
+#define MAC(...) V_(V_(MAC_X(__VA_ARGS__,3,2,_))(__VA_ARGS__))

+#define MAC_3(...) function_for_3(__VA_ARGS__)
 #define MAC_2(...) function_for_2(__VA_ARGS__)

For more than 1 argument, the MAC macro expands into MAC_2 (or MAC_3). But, for 1 or 0 arguments, the MAC macro expands to MAC__.

The MAC__ macro applies Jens' trick to detect whether it was passed 1 or 0 arguments. It does this with the E_ helper macro, and injecting its argument between E_ and the () that would cause it to be invoked. If there are 0 arguments, the E_ is invoked, and an argument is injected. The injected argument causes 0 to be the second argument for MAC_Y to select. If there is 1 argument, E_ is not expanded. The first argument to the to MAC_Y becomes E_ ... () 0 (where the ... is whatever the first argument was), and the second argument for MAC_Y is 1. This allows MAC_Y to invoke either MAC_0 or MAC_1.

jxh
  • 69,070
  • 8
  • 110
  • 193