45 votes

Peut macros être surchargé par le nombre d'arguments?

Comment ce travail? Comment un C99/C++11 variadic macro être mis en œuvre pour l'élargir à des choses différentes sur la seule base de la façon dont de nombreux arguments sont donnés à elle?

63voto

Potatoswatter Points 70305

Pour obtenir une surcharge de macro, nous avons d'abord besoin d'une macro qui sélectionne entre plusieurs implémentations. Cette partie n'est pas utiliser un variadic macro. Puis un variadic macro qui génériquement le nombre de ses arguments produit d'un sélecteur. Brancher l'argument de comptage dans un répartiteur produit une surcharge de macro.

Mise en garde: Ce système ne peut pas dire la différence entre le zéro et l'un des arguments parce qu'il n'y est pas de différence entre aucun argument, et un seul argument vide. Ils ressemblent à des MACRO().


Pour choisir entre les implémentations, utilisez la macro enchaînement de l'opérateur avec une série de fonctions comme les macros.

#define select( selector, ... ) impl ## _ ## selector( __VA_ARGS__ )
#define impl_1() meh
#define impl_2( abc, xyz ) # abc "wizza" xyz()
//etc

// usage: select( 1 ) => impl_1() => meh
//        select( 2, huz, bar ) => impl_2( huzza, bar ) => "huz" "wizza" bar()

Parce que l' ## opérateur supprime la macro d'extension de ses arguments, c'est mieux pour l'envelopper dans une autre macro.

#define CAT( A, B ) A ## B
#define SELECT( NAME, NUM ) CAT( NAME ## _, NUM )

À compter des arguments, l'utilisation __VA_ARGS__ de maj arguments comme (c'est l'astuce):

#define GET_COUNT( _1, _2, _3, _4, _5, _6 /* ad nauseam */, COUNT, ... ) COUNT
#define VA_SIZE( ... ) GET_COUNT( __VA_ARGS__, 6, 5, 4, 3, 2, 1 )

De les mettre ensemble:

#define CAT( A, B ) A ## B
#define SELECT( NAME, NUM ) CAT( NAME ## _, NUM )

#define GET_COUNT( _1, _2, _3, _4, _5, _6 /* ad nauseam */, COUNT, ... ) COUNT
#define VA_SIZE( ... ) GET_COUNT( __VA_ARGS__, 6, 5, 4, 3, 2, 1 )

#define VA_SELECT( NAME, ... ) SELECT( NAME, VA_SIZE(__VA_ARGS__) )(__VA_ARGS__)

Utilisation:

#define MY_OVERLOADED( ... ) VA_SELECT( MY_IMPL, __VA_ARGS__ )
#define MY_IMPL_1( X ) foo< X >
#define MY_IMPL_2( X, Y ) bar< X >( Y )
#define MY_IMPL_3( X, Y, Z ) bang_ ## X< Y >.Z()

4voto

Steven Lu Points 8021

Je poste comme un commentaire à Potatoswatter post, mais c'est trop long et nécessite un code d'inscription.

Voici un peu de code perl pour générer un ensemble de macros qui sont destinés à être surchargé de macros.

$ perl -le 'map{
        $arity = $_; map {
                $ar = 2 + $arity + $_; $arm = $ar - 1; $arlist = join("", map{"A$_, "} 1..$arity); $warlist = "WHAT, $arlist";
                @li = map {"_$_"} 0..$_; $lis = join(", ", @li); $lim = pop @li; $lims = join(", ", @li);
                print "#define FEI_${arity}A_$ar($warlist$lis) FEI_${arity}A_$arm($warlist$lims) WHAT($_, $arlist$lim)"
        } 1..3; print ""
} 0..4'

Voici la sortie du script:

#define FEI_0A_3(WHAT, _0, _1) FEI_0A_2(WHAT, _0) WHAT(1, _1)
#define FEI_0A_4(WHAT, _0, _1, _2) FEI_0A_3(WHAT, _0, _1) WHAT(2, _2)
#define FEI_0A_5(WHAT, _0, _1, _2, _3) FEI_0A_4(WHAT, _0, _1, _2) WHAT(3, _3)

#define FEI_1A_4(WHAT, A1, _0, _1) FEI_1A_3(WHAT, A1, _0) WHAT(1, A1, _1)
#define FEI_1A_5(WHAT, A1, _0, _1, _2) FEI_1A_4(WHAT, A1, _0, _1) WHAT(2, A1, _2)
#define FEI_1A_6(WHAT, A1, _0, _1, _2, _3) FEI_1A_5(WHAT, A1, _0, _1, _2) WHAT(3, A1, _3)

#define FEI_2A_5(WHAT, A1, A2, _0, _1) FEI_2A_4(WHAT, A1, A2, _0) WHAT(1, A1, A2, _1)
#define FEI_2A_6(WHAT, A1, A2, _0, _1, _2) FEI_2A_5(WHAT, A1, A2, _0, _1) WHAT(2, A1, A2, _2)
#define FEI_2A_7(WHAT, A1, A2, _0, _1, _2, _3) FEI_2A_6(WHAT, A1, A2, _0, _1, _2) WHAT(3, A1, A2, _3)

#define FEI_3A_6(WHAT, A1, A2, A3, _0, _1) FEI_3A_5(WHAT, A1, A2, A3, _0) WHAT(1, A1, A2, A3, _1)
#define FEI_3A_7(WHAT, A1, A2, A3, _0, _1, _2) FEI_3A_6(WHAT, A1, A2, A3, _0, _1) WHAT(2, A1, A2, A3, _2)
#define FEI_3A_8(WHAT, A1, A2, A3, _0, _1, _2, _3) FEI_3A_7(WHAT, A1, A2, A3, _0, _1, _2) WHAT(3, A1, A2, A3, _3)

#define FEI_4A_7(WHAT, A1, A2, A3, A4, _0, _1) FEI_4A_6(WHAT, A1, A2, A3, A4, _0) WHAT(1, A1, A2, A3, A4, _1)
#define FEI_4A_8(WHAT, A1, A2, A3, A4, _0, _1, _2) FEI_4A_7(WHAT, A1, A2, A3, A4, _0, _1) WHAT(2, A1, A2, A3, A4, _2)
#define FEI_4A_9(WHAT, A1, A2, A3, A4, _0, _1, _2, _3) FEI_4A_8(WHAT, A1, A2, A3, A4, _0, _1, _2) WHAT(3, A1, A2, A3, A4, _3)

Ce sont les (régulièrement structuré sections de) groupes de macro surcharges qui sont utilisés pour générer FOR_EACH (un.k.un. FE) des macros qui peut envoyer une WHAT macro éventuellement avec un nombre arbitraire de querelles constantes (A1, A2...) en plus d'un nombre arbitraire d'arguments dans une liste, avec un index dans le bon de commande (un naïf mise en œuvre sans l'aide de quelque chose comme SELECT pour la surcharge serait de rendement inversée indices).

Par exemple, le reste de l'article (la nonregular "cas de base" partie de la deuxième bloc) se présente comme suit:

#define FE_INDEXED_1ARG(...) VA_SELECT(FEI_1A, __VA_ARGS__)
#define FEI_1A_3(WHAT, A1, _0) WHAT(0, A1, _0)

L'utilité de ce qui peut être mis en doute (je l'ai construit parce que j'ai vu utiliser pour ça...), et ni est-ce répondre à l'OP, la question directement (en fait, il a en quelque sorte fait le contraire-une structure foreach fait la même chose à tous les variadic arguments...), mais je pensais juste que la technique est assez intéressant (ainsi que absolument horrible à certains égards) et permet pour certains de la puissance expressive en utilisant le préprocesseur et il sera possible de générer très efficace machine de code de cette manière. Je pense que c'est aussi un exemple poignant de pourquoi, personnellement, je pense que le préprocesseur C a encore de la place pour l'amélioration.

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