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 pas toujours aussi simplement que ça:
FOO(std::map<int, int>, map_var); // error: macro "FOO" passed 3 arguments, but takes just 2
Bien sûr, nous pourrions 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. Les incompatibilités de type Plus doivent être traitées. Une idée comment résoudre ce problème avec une macro?
Réponses:
Parce que les supports d'angle peuvent également représenter (ou se produire dans) les opérateurs de comparaison
<
,>
,<=
et>=
, l' expansion macro ne peuvent pas ignorer les virgules à l' intérieur crochets comme il le fait entre 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 macro entre parenthèses:Le problème est alors que le paramètre reste entre parenthèses dans le développement 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 d'un nom de type entre parenthèses à l'aide d'un type de fonction:
Étant donné que la formation des types de fonction ignore les parenthèses supplémentaires, vous pouvez utiliser cette macro avec ou sans parenthèses où le nom du type n'inclut pas de virgule:
En C, bien sûr, ce n'est pas nécessaire car les noms de type ne peuvent pas contenir de virgules en dehors des parenthèses. Donc, pour une macro multilingue, vous pouvez écrire:
la source
template<class KeyType, class ValueType> void SomeFunc(FOO(std::map<KeyType, ValueType>) element) {}
si j'applique cette solution ici, les structures derrière la macro deviennent des types dépendants, et le préfixe du nom de type est maintenant requis sur le type. Vous pouvez l'ajouter, mais la déduction de type a été interrompue, vous devez donc maintenant lister manuellement les arguments de type pour appeler la fonction. J'ai fini par utiliser la méthode du temple pour définir une macro pour la virgule. Cela n'a peut-être pas l'air aussi joli, mais cela a parfaitement fonctionné.[]
et{}
, elles ne le sont pas, cela ne fonctionne que()
malheureusement. Voir: Cependant, il n'y a pas d'exigence pour les crochets ou les accolades pour équilibrer ...#define PROTECT(...) argument_type<void(__VA_ARGS__)>::type
. Le passage d'arguments est désormais facilement possible même via plusieurs macros et pour les types simples, vous pouvez ignorer PROTECT. Cependant, les types de fonction deviennent des pointeurs de fonction lorsqu'ils sont évalués comme ceciSi vous ne pouvez pas utiliser de parenthèses et que vous n'aimez pas la solution SINGLE_ARG de Mike, définissez simplement un COMMA:
Cela aide également si vous souhaitez stringifier certains des arguments de macro, comme dans
qui imprime
std::map<int , int> has typeid name "St3mapIiiSt4lessIiESaISt4pairIKiiEEE"
.la source
#define STRVX(...) STRV(__VA_ARGS__)
et#define STRV(...) # __VA_ARGS__
, puisstd::cout << STRV(type<A COMMA B>) << std::endl;
imprimeratype<A COMMA B>
etstd::cout << STRVX(type<A COMMA B>) << std::endl;
imprimeratype<A , B>
. (STRV
est pour "variadic stringify", etSTRVX
est pour "étendu variadic stringify".)COMMA
macro en premier lieu. C'est ce avec quoi j'ai fini.Si votre préprocesseur prend en charge les macros variadiques:
Sinon, c'est un peu plus fastidieux:
la source
Définissez simplement
FOO
commePuis invoquez-le toujours avec des parenthèses autour de l'argument type, par exemple
Il peut bien sûr être une bonne idée d'illustrer les invocations dans un commentaire sur la définition de la macro.
la source
UNPACK
faire quand on l'utilise comme ça) UNPACK type name
? Pourquoitype
obtient correctement le type lorsqu'il est utilisé) UNPACK type name
? Mais qu'est-ce qui se passe ici?Il existe au moins deux façons de procéder. Tout d'abord, vous pouvez définir une macro qui prend plusieurs arguments:
si vous faites cela, vous constaterez peut-être que vous finissez par définir plus de macros pour gérer plus d'arguments.
Deuxièmement, vous pouvez mettre des parenthèses autour de l'argument:
si vous faites cela, vous constaterez peut-être que les parenthèses supplémentaires gâchent la syntaxe du résultat.
la source
Ceci est possible avec P99 :
Le code ci-dessus supprime en fait uniquement la dernière virgule de la liste d'arguments. Vérifiez avec
clang -E
(P99 nécessite un compilateur C99).la source
La réponse simple est que vous ne pouvez pas. C'est un effet secondaire du choix des
<...>
arguments de modèle; les<
et>
apparaissent également dans des contextes déséquilibrés, de sorte que le mécanisme de macro ne peut pas être étendu pour les gérer comme il gère les parenthèses. (Certains membres du comité avaient plaidé pour un jeton différent, par exemple(^...^)
, mais ils n'ont pas été en mesure de convaincre la majorité des problèmes d'utilisation<...>
.)la source
(^...^)
c'est un visage heureux :)