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 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__
Handling Parantheses and commas with the preprocessor
Preprocessor
<stdarg.h>

Index

Fatal error: Uncaught PDOException: SQLSTATE[HY000]: General error: 8 attempt to write a readonly database in /home/httpd/vhosts/renenyffenegger.ch/php/web-request-database.php:78 Stack trace: #0 /home/httpd/vhosts/renenyffenegger.ch/php/web-request-database.php(78): PDOStatement->execute(Array) #1 /home/httpd/vhosts/renenyffenegger.ch/php/web-request-database.php(30): insert_webrequest_('/notes/developm...', 1758209359, '216.73.216.150', 'Mozilla/5.0 App...', NULL) #2 /home/httpd/vhosts/renenyffenegger.ch/httpsdocs/notes/development/languages/C-C-plus-plus/preprocessor/macros/__VA_ARGS__/index(183): insert_webrequest() #3 {main} thrown in /home/httpd/vhosts/renenyffenegger.ch/php/web-request-database.php on line 78