Ajout à __VA_ARGS__

Je sais que je peux le faire:

#define MACRO(api, ...) \ bool ret = api(123, ##__VA_ARGS__); 

Ceci est juste un exemple, cela fait partie d’une solution plus compliquée. Le fait est que je dois append le nombre variable d’arguments au premier 123. Le ## fait en sorte que le compilateur supprime la virgule après l’argument 123 si aucun argument n’a été passé dans MACRO.

Mais maintenant, je veux annexer les arguments à api, comme ceci:

 #define MACRO(api, ...) \ bool ret = api(__VA_ARGS__##, 456); 

Ne peut faire. Une solution consiste à avoir deux macros, MACRO et MACRO_V, par exemple, et à ce que la version _V ne traite aucun argument. Mais existe-t-il un moyen de le faire fonctionner avec une seule macro?

Oui, vous pouvez. Ce qui suit prend en charge jusqu’à 4 arguments, mais il peut être développé de manière sortingviale pour en supporter davantage:

 #define MACRO(api, ...) \ bool ret = api(__VA_ARGS__ VA_COMMA(__VA_ARGS__) 456) /* * VA_COMMA() expands to nothing if given no arguments and a comma if * given 1 to 4 arguments. Bad things happen if given more than 4 * arguments. Don't do it. */ #define VA_COMMA(...) GET_6TH_ARG(,##__VA_ARGS__,COMMA,COMMA,COMMA,COMMA,) #define GET_6TH_ARG(a1,a2,a3,a4,a5,a6,...) a6 #define COMMA , /* EXAMPLES */ MACRO(foo) /* bool ret = foo( 456) */ MACRO(foo,1) /* bool ret = foo(1 , 456) */ MACRO(foo,1,2,3,4) /* bool ret = foo(1,2,3,4 , 456) */ /* uh oh, too many arguments: */ MACRO(foo,1,2,3,4,5) /* bool ret = foo(1,2,3,4,5 5 456) */ MACRO(foo,1,2,3,4,5,6) /* bool ret = foo(1,2,3,4,5,6 5 456) */ 

Ce même truc est utilisé pour:

  • compter le nombre d’arguments
  • développer différemment en fonction du nombre d’arguments

Explication

VA_COMMA entoure ses arguments ( __VA_ARGS__ ) de six arguments supplémentaires: un argument vide avant (il n’est pas nécessaire qu’il soit vide, il est jeté) et quatre virgules et un argument vide après.

Ces six arguments ou plus sont passés à GET_6TH_ARG , qui, comme son nom l’indique, se développe au sixième argument. Tous les autres arguments sont ignorés.

Ainsi, MACRO(foo) est développé comme suit:

 step 0: MACRO(foo) step 1: bool ret = foo( VA_COMMA() 456) step 2: bool ret = foo( GET_6TH_ARG(,COMMA,COMMA,COMMA,COMMA,) 456) step 3: bool ret = foo( 456) 

MACRO(foo,1) est développé comme suit:

 step 0: MACRO(foo,1) step 1: bool ret = foo(1 VA_COMMA(1) 456) step 2: bool ret = foo(1 GET_6TH_ARG(,1,COMMA,COMMA,COMMA,COMMA,) 456) step 3: bool ret = foo(1 COMMA 456) step 4: bool ret = foo(1 , 456) 

MACRO(foo,1,2) est développé comme suit:

 step 0: MACRO(foo,1,2) step 1: bool ret = foo(1,2 VA_COMMA(1,2) 456) step 2: bool ret = foo(1,2 GET_6TH_ARG(,1,2,COMMA,COMMA,COMMA,COMMA,) 456) step 3: bool ret = foo(1,2 COMMA 456) step 4: bool ret = foo(1,2 , 456) 

MACRO(foo,1,2,3,4,5) est développé comme suit:

 step 0: MACRO(foo,1,2,3,4,5) step 1: bool ret = foo(1,2,3,4,5 VA_COMMA(1,2,3,4,5) 456) step 2: bool ret = foo(1,2,3,4,5 GET_6TH_ARG(,1,2,3,4,5,COMMA,COMMA,COMMA,COMMA,) 456) step 3: bool ret = foo(1,2,3,4,5 5 456) 

Le comportement de ## qui permet à cela de fonctionner dans le premier cas est une extension GCC (C99 ne permet pas à la partie argument variable d’être vide), et il s’applique spécifiquement au cas avec une virgule à gauche et __VA_ARGS__ sur la droite. Voir par exemple http://gcc.gnu.org/onlinedocs/gcc-4.5.1/gcc/Variadic-Macros.html#Variadic-Macros (au bas de la page).

Eh bien, je pense que c’est possible avec quelque chose comme ça:

 #define NO_FIRST(first, ...) __VA_ARGS__ #define DO_APPEND_LAST(last, ...) NO_FIRST(__VA_ARGS__, last) #define MACRO(api, ...) bool ret = api(DO_APPEND_LAST(456, dummy, ##__VA_ARGS__)); 

Je n’ai pas vérifié, mais devrait fonctionner dans les derniers VS et gcc.