98 votes

Virgule dans une macro C/C++

Disons que nous avons une macro comme celle-ci

#define FOO(type,name) type name

Que nous pourrions utiliser comme

FOO(int, int_var);

Mais ce n'est pas toujours aussi simple que cela :

FOO(std::map<int, int>, map_var); // error: macro "FOO" passed 3 arguments, but takes just 2

Bien sûr qu'on peut le faire :

 typedef std::map<int, int> map_int_int_t;
 FOO(map_int_int_t, map_var); // OK

ce qui n'est pas très ergonomique. De plus, il faut faire face aux incompatibilités de type. Une idée pour résoudre ce problème avec une macro ?

113voto

temple Points 519

Si vous ne pouvez pas utiliser de parenthèses et que vous n'aimez pas la solution SINGLE_ARG de Mike, définissez simplement une COMMA :

#define COMMA ,

FOO(std::map<int COMMA int>, map_var);

Ceci est également utile si vous voulez stringifier certains des arguments de la macro, comme dans

#include <cstdio>
#include <map>
#include <typeinfo>

#define STRV(...) #__VA_ARGS__
#define COMMA ,
#define FOO(type, bar) bar(STRV(type) \
    " has typeid name \"%s\"", typeid(type).name())

int main()
{
    FOO(std::map<int COMMA int>, std::printf);
}

qui imprime std::map<int , int> has typeid name "St3mapIiiSt4lessIiESaISt4pairIKiiEEE" .

104voto

ecatmur Points 64173

Parce que les crochets d'angle peuvent également représenter (ou apparaître dans) les opérateurs de comparaison < , > , <= y >= L'expansion des macros ne peut pas ignorer les virgules à l'intérieur des crochets comme elle le fait pour les parenthèses. (C'est également un problème pour les crochets et les accolades, même si ceux-ci se présentent généralement sous forme de paires équilibrées). Vous pouvez mettre l'argument de la macro entre parenthèses :

FOO((std::map<int, int>), map_var);

Le problème est alors que le paramètre reste entre parenthèses à l'intérieur de l'expansion de la macro, ce qui l'empêche d'être lu comme un type dans la plupart des contextes.

Une astuce intéressante pour contourner ce problème est qu'en C++, vous pouvez extraire un nom de type à partir d'un nom de type entre parenthèses en utilisant un type de fonction :

template<typename T> struct argument_type;
template<typename T, typename U> struct argument_type<T(U)> { typedef U type; };
#define FOO(t,name) argument_type<void(t)>::type name
FOO((std::map<int, int>), map_var);

Comme la formation des types de fonctions ignore les parenthèses supplémentaires, vous pouvez utiliser cette macro avec ou sans parenthèses lorsque le nom du type ne comporte pas de virgule :

FOO((int), int_var);
FOO(int, int_var2);

En C, bien sûr, ce n'est pas nécessaire car les noms de types ne peuvent pas contenir de virgules en dehors des parenthèses. Ainsi, pour une macro inter-langue, vous pouvez écrire :

#ifdef __cplusplus__
template<typename T> struct argument_type;
template<typename T, typename U> struct argument_type<T(U)> { typedef U type; };
#define FOO(t,name) argument_type<void(t)>::type name
#else
#define FOO(t,name) t name
#endif

55voto

Mike Seymour Points 130519

Si votre préprocesseur supporte les macros variadiques :

#define SINGLE_ARG(...) __VA_ARGS__
#define FOO(type,name) type name

FOO(SINGLE_ARG(std::map<int, int>), map_var);

Sinon, c'est un peu plus fastidieux :

#define SINGLE_ARG2(A,B) A,B
#define SINGLE_ARG3(A,B,C) A,B,C
// as many as you'll need

FOO(SINGLE_ARG2(std::map<int, int>), map_var);

4voto

Pete Becker Points 27371

Il y a au moins deux façons de le faire. Premièrement, vous pouvez définir une macro qui prend plusieurs arguments :

#define FOO2(type1, type2, name) type1, type2, name

Si vous faites cela, vous risquez de devoir définir plus de macros pour gérer plus d'arguments.

Deuxièmement, vous pouvez mettre des parenthèses autour de l'argument :

#define FOO(type, name) type name
F00((std::map<int, int>) map_var;

Si vous faites cela, vous risquez de constater que les parenthèses supplémentaires altèrent la syntaxe du résultat.

4voto

xiaq Points 167

Cela est possible avec P99 :

#include "p99/p99.h"
#define FOO(...) P99_ALLBUTLAST(__VA_ARGS__) P99_LAST(__VA_ARGS__)
FOO()

Le code ci-dessus ne supprime effectivement que la dernière virgule de la liste d'arguments. Vérifiez avec clang -E (P99 nécessite un compilateur C99).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X