Search notes:

Preprocessor: __VA_ARGS__ (Variadic macros)

Basics

The following example tries to demonstrate the basic functionality of variadic macros.
If the parameters of a macro contain three consecutive dots (...), this indicates that an arbitrary number of parameters may be passed.
In the macro expansion, these values that are then referenced by the special identifier __VA_ARGS__.
#define MACRO_VA_ARGS(...) RESULT(__VA_ARGS__)

MACRO_VA_ARGS()
MACRO_VA_ARGS(one)
MACRO_VA_ARGS(two,three)
MACRO_VA_ARGS(foo, bar, baz)
Github repository about-preprocessor, path: /macros/__VA_ARGS__/basics.c
When this source file is preprocessed (gcc -E -P basics.c or cl -nologo -EP basics.c), it prints
RESULT()
RESULT(one)
RESULT(two,three)
RESULT(foo, bar, baz)

Compiler differences

Apparently, the GNU compiler treats the expansion of __VA_ARGS__ differently from the Visual C Compiler, as the following example shows:
#define MACRO_WITH_3_PARAMS(p1, p2, p3) P1 = p1 | P2 = p2 | P3 = p3

#define MACRO_VA_ARGS(...) MACRO_WITH_3_PARAMS( __VA_ARGS__)

MACRO_VA_ARGS(foo, bar, baz)
Github repository about-preprocessor, path: /macros/__VA_ARGS__/compiler-differences.c
When preprocessed with the GNU compiler, it prints, as probably desired:
P1 = foo | P2 = bar | P3 = baz
However, when preprocessed with Microsoft's compiler, it emits the warning C4003: not enough arguments for function-like macro invocation 'MACRO_WITH_3_PARAMS' and prints:
P1 = foo, bar, baz | P2 =  | P3 =
That is: Microsoft's preprocessor expands everything into one parameter of MACRO_WITH_3_PARAMS.
In order for both preprocessors to emit the text, another macro needs to be defined that just passes on what it receives. This macro (PASS_ON) is then applied on a macro name (MACRO_WITH_3_PARAMS) so that the macro name dos not get expanded and a second time with the parameters:
#define MACRO_WITH_3_PARAMS(p1, p2, p3) P1 = p1 | P2 = p2 | P3 = p3

#define PASS_ON(...) __VA_ARGS__
#define MACRO_VA_ARGS(...) PASS_ON(PASS_ON(MACRO_WITH_3_PARAMS)( __VA_ARGS__))

MACRO_VA_ARGS(foo, bar, baz)
Github repository about-preprocessor, path: /macros/__VA_ARGS__/compiler-differences-solved.c
With this «solution», both preprocessors now emit:
P1 = foo | P2 = bar | P3 = baz

Simple example

The following simple example uses __VA_ARGS__ to create a macro that takes a variable number of arguments with the three dots. The variable number of arguments that are represented by the dots are passed to __VA_ARGS__:
#include <stdio.h>

#define TQ84_PRINTF(FORMAT, ...) printf("tq84: " FORMAT "\n", __VA_ARGS__)

int main() {

  TQ84_PRINTF("%d", 42);
  TQ84_PRINTF("%s, %s", "Hello", "world");
  TQ84_PRINTF("%d %f %s", 99, 3.14, "An int and a float");

}
Github repository about-cpp, path: /preprocessor/macros/__VA_ARGS__/simple.c

Visual compiler (cl) vs Gnu compiler (gcc)

The following file was preprocessed with the Visual C compiler (cl /EP basic) and the GNU compiler (gcc -x c -E -P basic) to demonstrate a difference between their preprocessors.
                                      //  gcc          |  cl
// ---------------------------------  //  -----------  +  --------------------------------
#define A(...)         __VA_ARGS__    //               |
A                                     //  A            |  A
A()                                   //               |
A(1)                                  //  1            |  1
A(1,2)                                //  1,2          |  1,2
                                      //               |
#define B(...)    A(  __VA_ARGS__)    //               |
B                                     //  B            |  B
B()                                   //               |
B(one)                                //  one          |  one
B(one,two)                            //  one,two      |  one,two
                                      //               |
#define C(x, ...) A(x,__VA_ARGS__)    //               |
C                                     //  C            |  C
C()                                   //  ,            |  ,          basic(17): warning C4003: not enough arguments for function-like macro invocation 'C'
C(i)                                  //  i,           |  i,
C(ii,iii)                             //  ii,iii       |  ii,iii
C(iv,v,vi)                            //  iv,v,vi      |  iv,v,vi
                                      //               |
#define D(x, ...) Q(x,__VA_ARGS__)    //               |
D                                     //  D            |  D
D()                                   //  Q(,)         |  Q( )       basic(24): warning C4003: not enough arguments for function-like macro invocation 'D'
D(a)                                  //  Q(a,)        |  Q(a )
D(b,c)                                //  Q(b,c)       |  Q(b,c)
D(d,e,f)                              //  Q(d,e,f)     |  Q(d,e,f)
Github repository about-cpp, path: /preprocessor/macros/__VA_ARGS__/basic
When using the token paste operator (##), these differences go away:
#define WITH_TP(x, y, ...)    with_tp(x, y, ## __VA_ARGS__)
#define WITHOUT_TP(x, y, ...) without_tp(x, y,  __VA_ARGS__)

WITH_TP(one, two)
WITH_TP(three, four, five)

WITHOUT_TP(1, 2)
WITHOUT_TP(3, 4, 5)
Github repository about-cpp, path: /preprocessor/macros/__VA_ARGS__/token-paste-operator

See also

count arguments in __VA_ARGS__
Preprocessor
<stdarg.h>

Index