Paramètres facultatifs avec les macros C ++

105

Existe-t-il un moyen d'obtenir des paramètres facultatifs avec les macros C ++? Une sorte de surcharge serait bien aussi.

Cénoc
la source
1
Idem pour C: stackoverflow.com/questions/11761703/… Devrait être le même puisque les préprocesseurs sont fondamentalement les mêmes: stackoverflow.com/questions/5085533/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
Peut-être que vous recherchez des surcharges de fonctions, des paramètres par défaut, des modèles variadiques ou peut-être l'idiome de paramètre nommé
smoothware
Veuillez mettre à jour votre réponse sélectionnée en fonction des réponses les plus votées avec des solutions réelles, et non des réponses les plus modestesNo you can't
Albert Renshaw

Réponses:

158

Voici une façon de le faire. Il utilise la liste d'arguments deux fois, d'abord pour former le nom de la macro d'assistance, puis pour transmettre les arguments à cette macro d'assistance. Il utilise une astuce standard pour compter le nombre d'arguments d'une macro.

enum
{
    plain = 0,
    bold = 1,
    italic = 2
};

void PrintString(const char* message, int size, int style)
{
}

#define PRINT_STRING_1_ARGS(message)              PrintString(message, 0, 0)
#define PRINT_STRING_2_ARGS(message, size)        PrintString(message, size, 0)
#define PRINT_STRING_3_ARGS(message, size, style) PrintString(message, size, style)

#define GET_4TH_ARG(arg1, arg2, arg3, arg4, ...) arg4
#define PRINT_STRING_MACRO_CHOOSER(...) \
    GET_4TH_ARG(__VA_ARGS__, PRINT_STRING_3_ARGS, \
                PRINT_STRING_2_ARGS, PRINT_STRING_1_ARGS, )

#define PRINT_STRING(...) PRINT_STRING_MACRO_CHOOSER(__VA_ARGS__)(__VA_ARGS__)

int main(int argc, char * const argv[])
{
    PRINT_STRING("Hello, World!");
    PRINT_STRING("Hello, World!", 18);
    PRINT_STRING("Hello, World!", 18, bold);

    return 0;
}

Cela facilite la tâche à l'appelant de la macro, mais pas à l'écrivain.

Derek Ledbetter
la source
1
C'est plutôt cool, mais je ne pense pas que cela fonctionnerait si je faisais juste PRINT_STRING. Dans ce cas, il n'y aurait pas d'impression par défaut (et c'est en fait le cas que je veux utiliser). Toujours +1 pour vraiment cool.
Cenoc
2
fonctionne pour moi dans gcc (et c'est très intelligent!) :-) mais ne fonctionne pas pour moi dans Visual Studio :-(
Tim Gradwell
3
@TimGradwell - c'est dû à un bogue dans le compilateur MSVC qu'ils ont reconnu mais n'ont pas corrigé depuis près d'une décennie. Cependant, des solutions de contournement sont disponibles .
BeeOnRope
Intelligent, mais ne fonctionne pas pour les arguments macro variadiques optionnels à cause de la chose 'push out' que vous avez en cours dans `GET_4th_ARG '.
searchengine27
est-ce PRINT_STRING_MACRO_CHOOSERmême nécessaire? Puis-je remplacer directement par son corps intérieur et appeler tout cela avec (__VA_ARGS__)?
Herrgott
85

Avec beaucoup de respect à Derek Ledbetter pour sa réponse - et avec mes excuses pour avoir relancé une vieille question.

Comprendre ce qu'il faisait et reprendre ailleurs sur la capacité de précéder le __VA_ARGS__avec ##m'a permis de proposer une variation ...

// The multiple macros that you would need anyway [as per: Crazy Eddie]
#define XXX_0()                     <code for no arguments> 
#define XXX_1(A)                    <code for one argument> 
#define XXX_2(A,B)                  <code for two arguments> 
#define XXX_3(A,B,C)                <code for three arguments> 
#define XXX_4(A,B,C,D)              <code for four arguments>  

// The interim macro that simply strips the excess and ends up with the required macro
#define XXX_X(x,A,B,C,D,FUNC, ...)  FUNC  

// The macro that the programmer uses 
#define XXX(...)                    XXX_X(,##__VA_ARGS__,\
                                          XXX_4(__VA_ARGS__),\
                                          XXX_3(__VA_ARGS__),\
                                          XXX_2(__VA_ARGS__),\
                                          XXX_1(__VA_ARGS__),\
                                          XXX_0(__VA_ARGS__)\
                                         ) 

Pour les non-experts comme moi qui tombent sur la réponse, mais ne peuvent pas vraiment voir comment cela fonctionne, je vais passer en revue le traitement réel, en commençant par le code suivant ...

XXX();
XXX(1); 
XXX(1,2); 
XXX(1,2,3); 
XXX(1,2,3,4); 
XXX(1,2,3,4,5);      // Not actually valid, but included to show the process 

Devient...

XXX_X(, XXX_4(), XXX_3(),  XXX_2(),    XXX_1(),      XXX_0()         );
XXX_X(, 1,       XXX_4(1), XXX_3(1),   XXX_2(1),     XXX_1(1),       XXX_0(1)          );
XXX_X(, 1,       2,        XXX_4(1,2), XXX_3(1,2),   XXX_2(1,2),     XXX_1(1,2),       XXX_0(1,2)        );
XXX_X(, 1,       2,        3,          XXX_4(1,2,3), XXX_3(1,2,3),   XXX_2(1,2,3),     XXX_1(1,2,3),     XXX_0(1,2,3)      );
XXX_X(, 1,       2,        3,          4,            XXX_4(1,2,3,4), XXX_3(1,2,3,4),   XXX_2(1,2,3,4),   XXX_1(1,2,3,4),   XXX_0(1,2,3,4)    );
XXX_X(, 1,       2,        3,          4,            5,              XXX_4(1,2,3,4,5), XXX_3(1,2,3,4,5), XXX_2(1,2,3,4,5), XXX_1(1,2,3,4,5), XXX_0(1,2,3,4,5) );

Ce qui devient juste le sixième argument ...

XXX_0(); 
XXX_1(1); 
XXX_2(1,2); 
XXX_3(1,2,3); 
XXX_4(1,2,3,4); 
5; 

PS: Supprimez le #define pour XXX_0 pour obtenir une erreur de compilation [par exemple: si une option sans argument n'est pas autorisée].

PPS: Ce serait bien que les situations invalides (par exemple: 5) soient quelque chose qui donne une erreur de compilation plus claire au programmeur!

PPPS: Je ne suis pas un expert, donc je suis très heureux d'entendre des commentaires (bons, mauvais ou autres)!

David Sorkovsky
la source
3
Vous pouvez obtenir une erreur de compilation claire si vous convertissez l'argument sélectionné qui est supposé être un nom MACRO en chaîne en utilisant # (le signe dièse) et comparez ses n premiers caractères avec le préfixe attendu et s'il n'y a pas de correspondance, imprimez un message informatif Erreur.
AturSams
1
Wow, je ne sais pas si cela fonctionne, mais c'est au moins très créatif!
Expiation limitée le
4
pourquoi le premier argument est-il toujours vide? pourquoi ne pouvons-nous simplement l'omettre: XXX_X(,##__VA_ARGS__,` ... XXX_X (, XXX_4 (), XXX_3 (), XXX_2 (), XXX_1 (), XXX_0 ()); `
rahman
2
Le premier argument vide (virgule) est important. ## __ VA_ARGS__ s'il est précédé d'une virgule - il supprime la virgule si ## __ VA_ARGS__ se développe en rien. Vous pouvez le voir dans l'exemple "Devient ..." car la première ligne (sans arguments) n'a que 6 paramètres mais le reste en obtient 7. Cette astuce garantit que la situation sans arguments fonctionne
David Sorkovsky
@Eric - c'est dû à un bogue dans les compilateurs Microsoft, mais vous pouvez voir cette question pour des solutions de contournement.
BeeOnRope
31

Les macros C ++ n'ont pas changé depuis C. Comme C n'avait pas de surcharge et d'arguments par défaut pour les fonctions, il n'en avait certainement pas pour les macros. Donc pour répondre à votre question: non, ces fonctionnalités n'existent pas pour les macros. Votre seule option est de définir plusieurs macros avec des noms différents (ou de ne pas utiliser de macros du tout).

Remarque: en C ++, il est généralement considéré comme une bonne pratique de s'éloigner le plus possible des macros. Si vous avez besoin de fonctionnalités comme celle-ci, il y a de fortes chances que vous abusiez des macros.

sepp2k
la source
4
Notez que la raison pour laquelle il est impossible de "surcharger" les macros est qu'elles n'ont aucun type inhérent. Les macros sont simplement développées.
mk12
2
Bien que j'utilise le moins de macros possible, j'ai trouvé que le débogage via la sortie de trace devient un peu plus facile avec des choses comme __FILE__et __LINE__et autres ...
Christian Severin
pas une bonne réponse. c'est une bonne réponse: stackoverflow.com/q/27049491/893406
v.oddou
La compilation conditionnelle et le débogage / journalisation sont le domaine où les macros sont vraiment pratiques et légitimes. Tout programmeur sérieux le sait. Ce qui est une bonne pratique, c'est d'éviter d'utiliser des macros pour définir des constantes et de faire du codage de niveau C fou pour créer des modèles de conteneurs. Je souhaite que C ++ ajoute également plus de fonctionnalités aux macros. Ils sont orthogonaux aux modèles. Le mieux serait bien sûr les codelets qui me permettent d'ajouter des générateurs dans le compilateur pour le langage spécifique au domaine (aspects).
Lothar
1
Je pense également que ce n'est pas une bonne réponse, car une macro est quelque chose de complètement différent de toute option de langage C ++, car elle sera gérée AVANT le compilateur. Vous pouvez donc faire autre chose, et aucun compilateur ou éditeur de liens ne doit optimiser le code, car ce n'est peut-être pas à optimiser.
alabamajack
26

Avec le plus grand respect à Derek Ledbetter , David Sorkovsky , Syphorlate pour leurs réponses, ainsi que la méthode ingénieuse pour détecter les macro-arguments vides de Jens Gustedt à

https://gustedt.wordpress.com/2010/06/08/detect-empty-macro-arguments/

enfin je viens avec quelque chose qui incorpore toutes les astuces, de sorte que la solution

  1. Utilise uniquement des macros C99 standard pour réaliser une surcharge de fonction, aucune extension GCC / CLANG / MSVC impliquée (c'est-à-dire, une virgule avalée par l'expression spécifique , ##__VA_ARGS__pour GCC / CLANG et une avalée implicite par ##__VA_ARGS__pour MSVC). Alors n'hésitez pas à transmettre le manquant --std=c99à votre compilateur si vous le souhaitez =)
  2. Fonctionne pour zéro argument , ainsi qu'un nombre illimité d'arguments , si vous l'étendez davantage pour répondre à vos besoins
  3. Fonctionne raisonnablement multiplateforme , au moins testé pour

    • GNU / Linux + GCC (GCC 4.9.2 sur CentOS 7.0 x86_64)
    • GNU / Linux + CLANG / LLVM , (CLANG / LLVM 3.5.0 sur CentOS 7.0 x86_64)
    • OS X + Xcode , (XCode 6.1.1 sur OS X Yosemite 10.10.1)
    • Windows + Visual Studio , (Visual Studio 2013 Update 4 sur Windows 7 SP1 64 bits)

Pour les paresseux, passez simplement au tout dernier de cet article pour copier la source. Vous trouverez ci-dessous l'explication détaillée qui, espérons-le, aide et inspire toutes les personnes à la recherche de __VA_ARGS__solutions générales comme moi. =)

Voici comment ça se passe. Tout d' abord définir la « fonction » surcharge utilisateur visible, je l' ai appelé create, et la définition de la fonction réelle liée realCreate, et les définitions de macros avec nombre différent d'arguments CREATE_2, CREATE_1, CREATE_0comme indiqué ci - dessous:

#define create(...) MACRO_CHOOSER(__VA_ARGS__)(__VA_ARGS__)

void realCreate(int x, int y)
{
  printf("(%d, %d)\n", x, y);
}

#define CREATE_2(x, y) realCreate(x, y)
#define CREATE_1(x) CREATE_2(x, 0)
#define CREATE_0() CREATE_1(0)

La MACRO_CHOOSER(__VA_ARGS__)partie résout finalement les noms de définition de macro, et la deuxième (__VA_ARGS__)partie comprend leurs listes de paramètres. Ainsi, l'appel d'un utilisateur à se create(10)résout à CREATE_1(10), la CREATE_1pièce vient de MACRO_CHOOSER(__VA_ARGS__), et la (10)pièce vient de la seconde (__VA_ARGS__).

Le MACRO_CHOOSERutilise l'astuce que, si elle __VA_ARGS__est vide, l'expression suivante est concaténée en un appel de macro valide par le préprocesseur:

NO_ARG_EXPANDER __VA_ARGS__ ()  // simply shrinks to NO_ARG_EXPANDER()

Ingénieusement, nous pouvons définir cet appel de macro résultant comme

#define NO_ARG_EXPANDER() ,,CREATE_0

Notez les deux virgules, elles sont expliquées bientôt. La prochaine macro utile est

#define MACRO_CHOOSER(...) CHOOSE_FROM_ARG_COUNT(NO_ARG_EXPANDER __VA_ARGS__ ())

donc les appels de

create();
create(10);
create(20, 20);

sont en fait étendus à

CHOOSE_FROM_ARG_COUNT(,,CREATE_0)();
CHOOSE_FROM_ARG_COUNT(NO_ARG_EXPANDER 10 ())(10);
CHOOSE_FROM_ARG_COUNT(NO_ARG_EXPANDER 20, 20 ())(20, 20);

Comme le nom de la macro l'indique, nous devons compter le nombre d'arguments plus tard. Voici une autre astuce: le préprocesseur ne fait qu'un simple remplacement de texte. Il déduit le nombre d'arguments d'un appel de macro simplement à partir du nombre de virgules qu'il voit à l'intérieur des parenthèses. Les "arguments" réels séparés par des virgules n'ont pas besoin d'être de syntaxe valide. Ils peuvent être n'importe quel texte. C'est-à-dire que, dans l'exemple ci-dessus, NO_ARG_EXPANDER 10 ()est compté comme 1 argument pour l'appel du milieu. NO_ARG_EXPANDER 20et 20 ()sont comptés comme 2 arguments pour l'appel du bas respectivement.

Si nous utilisons les macros d'assistance suivantes pour les développer davantage

##define CHOOSE_FROM_ARG_COUNT(...) \
  FUNC_RECOMPOSER((__VA_ARGS__, CREATE_2, CREATE_1, ))
#define FUNC_RECOMPOSER(argsWithParentheses) \
  FUNC_CHOOSER argsWithParentheses

La fin ,après CREATE_1est une solution de contournement pour GCC / CLANG, supprimant une erreur (faux positif) indiquant cela ISO C99 requires rest arguments to be usedlors du passage -pedanticà votre compilateur. Il FUNC_RECOMPOSERs'agit d'une solution de contournement pour MSVC, ou il ne peut pas compter correctement le nombre d'arguments (c'est-à-dire, des virgules) entre les parenthèses des appels de macro. Les résultats sont en outre résolus à

FUNC_CHOOSER (,,CREATE_0, CREATE_2, CREATE_1, )();
FUNC_CHOOSER (NO_ARG_EXPANDER 10 (), CREATE_2, CREATE_1, )(10);
FUNC_CHOOSER (NO_ARG_EXPANDER 20, 20 (), CREATE_2, CREATE_1, )(20, 20);

Comme vous l'avez peut-être vu, la dernière étape dont nous avons besoin est d'utiliser une astuce de comptage d'arguments standard pour enfin choisir les noms de version de macro voulus:

#define FUNC_CHOOSER(_f1, _f2, _f3, ...) _f3

qui résout les résultats à

CREATE_0();
CREATE_1(10);
CREATE_2(20, 20);

et nous donne certainement les appels de fonction souhaités et réels:

realCreate(0, 0);
realCreate(10, 10);
realCreate(20, 20);

En réunissant le tout, avec un réarrangement des instructions pour une meilleure lisibilité, toute la source de l'exemple à 2 arguments est ici:

#include <stdio.h>

void realCreate(int x, int y)
{
  printf("(%d, %d)\n", x, y);
}

#define CREATE_2(x, y) realCreate(x, y)
#define CREATE_1(x) CREATE_2(x, 0)
#define CREATE_0() CREATE_1(0)

#define FUNC_CHOOSER(_f1, _f2, _f3, ...) _f3
#define FUNC_RECOMPOSER(argsWithParentheses) FUNC_CHOOSER argsWithParentheses
#define CHOOSE_FROM_ARG_COUNT(...) FUNC_RECOMPOSER((__VA_ARGS__, CREATE_2, CREATE_1, ))
#define NO_ARG_EXPANDER() ,,CREATE_0
#define MACRO_CHOOSER(...) CHOOSE_FROM_ARG_COUNT(NO_ARG_EXPANDER __VA_ARGS__ ())
#define create(...) MACRO_CHOOSER(__VA_ARGS__)(__VA_ARGS__)

int main()
{
  create();
  create(10);
  create(20, 20);
  //create(30, 30, 30);  // Compilation error
  return 0;
}

Bien que compliqué, laid, pesant sur le développeur d'API, il existe une solution pour surcharger et définir les paramètres optionnels des fonctions C / C ++ pour nous les fous. L'utilisation des API surchargées sortantes devient très agréable et agréable. =)

S'il y a une autre simplification possible de cette approche, veuillez me le faire savoir à

https://github.com/jason-deng/C99FunctionOverload

Encore une fois un merci spécial à toutes les personnes brillantes qui m'ont inspiré et conduit à réaliser ce travail! =)

Jason Deng
la source
3
Comment étendre cela à 3 ou 4 fonctions?
Phylliida
@Phylliida ideone.com/jD0Hm5 - zéro à cinq arguments pris en charge.
xx
9

Pour quiconque recherche péniblement une solution VA_NARGS qui fonctionne avec Visual C ++. La macro suivante a fonctionné pour moi parfaitement (également avec zéro paramètre!) Dans Visual C ++ Express 2010:

#define VA_NUM_ARGS_IMPL(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,N,...) N
#define VA_NUM_ARGS_IMPL_(tuple) VA_NUM_ARGS_IMPL tuple
#define VA_NARGS(...)  bool(#__VA_ARGS__) ? (VA_NUM_ARGS_IMPL_((__VA_ARGS__, 24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1))) : 0

Si vous voulez une macro avec des paramètres facultatifs, vous pouvez faire:

//macro selection(vc++)
#define SELMACRO_IMPL(_1,_2,_3, N,...) N
#define SELMACRO_IMPL_(tuple) SELMACRO_IMPL tuple
#define mymacro1(var1) var1
#define mymacro2(var1,var2) var2*var1
#define mymacro3(var1,var2,var3) var1*var2*var3
#define mymacro(...) SELMACRO_IMPL_((__VA_ARGS__, mymacro3(__VA_ARGS__), mymacro2(__VA_ARGS__), mymacro1(__VA_ARGS__))) 

Cela a fonctionné pour moi aussi en vc. Mais cela ne fonctionne pas pour zéro paramètre.

int x=99;
x=mymacro(2);//2
x=mymacro(2,2);//4
x=mymacro(2,2,2);//8
Syphorlate
la source
Je reçoisunresolved external symbol _bool referenced in function _main
Avidan Borisov
oui cela peut arriver dans certains cas. vous devez être conscient que bool (#__ VA_ARGS__)? est différente des autres macros car elle est en cours d'évaluation au moment de l'exécution. selon votre cas, vous pouvez toutefois omettre cette partie du code.
Syphorlate
2
En fait, je me suis retrouvé avec pastebin.com/H3T75dcn qui fonctionne parfaitement (0 argument aussi).
Avidan Borisov
Merci pour le lien, et oui vous pouvez le faire en utilisant aussi sizeof mais pour moi cela n'a pas fonctionné dans certains cas mais le principe est le même (évaluation booléenne).
Syphorlate
Pourriez-vous donner des exemples où cela échoue?
Avidan Borisov
7

gcc/ g++prend en charge les macros varargs mais je ne pense pas que ce soit standard, alors utilisez-le à vos risques et périls.

Paul R
la source
4
Ils sont standard dans C99 et ils sont également ajoutés à C ++ 0x.
greyfade
5
#include <stdio.h>

#define PP_NARG(...) \
    PP_NARG_(__VA_ARGS__,PP_RSEQ_N())
#define PP_NARG_(...) \
    PP_ARG_N(__VA_ARGS__)
#define PP_ARG_N( \
    _1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \ 
    _11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \
    _21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \
    _31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \
    _41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \
    _51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \
    _61,_62,_63,N,...) N
#define PP_RSEQ_N() \
    63,62,61,60,                   \
    59,58,57,56,55,54,53,52,51,50, \
    49,48,47,46,45,44,43,42,41,40, \
    39,38,37,36,35,34,33,32,31,30, \
    29,28,27,26,25,24,23,22,21,20, \
    19,18,17,16,15,14,13,12,11,10, \
    9,8,7,6,5,4,3,2,1,0

#define PP_CONCAT(a,b) PP_CONCAT_(a,b)
#define PP_CONCAT_(a,b) a ## b

#define THINK(...) PP_CONCAT(THINK_, PP_NARG(__VA_ARGS__))(__VA_ARGS__)
#define THINK_0() THINK_1("sector zz9 plural z alpha")
#define THINK_1(location) THINK_2(location, 42)
#define THINK_2(location,answer) THINK_3(location, answer, "deep thought")
#define THINK_3(location,answer,computer) \
  printf ("The answer is %d. This was calculated by %s, and a computer to figure out what this"
          " actually means will be build in %s\n", (answer), (computer), (location))

int
main (int argc, char *argv[])
{
  THINK (); /* On compilers other than GCC you have to call with least one non-default argument */
}

AVERTISSEMENT: Surtout inoffensif.

Joe D
la source
il y a une erreur dans votre code. s'il vous plaît faire :%s/MY_MACRO_/THINK_/g:)
João Portela
également, cela ne fonctionnait pas avec zéro argument en utilisant g ++i686-apple-darwin10-g++-4.2.1 (GCC) 4.2.1 (Apple Inc. build 5664)
João Portela
1
Aucun argument n'existe pour les macros variadiaques, car le jeton vide est un espace réservé valide.
Paul Fultz II
3

Ce n'est pas vraiment ce pour quoi le préprocesseur est conçu.

Cela dit, si vous voulez entrer dans le domaine de la programmation de macros sérieusement difficile avec un minimum de lisibilité, vous devriez jeter un coup d'œil à la bibliothèque de préprocesseurs Boost . Après tout, ce ne serait pas C ++ s'il n'y avait pas trois niveaux de programmation totalement compatibles avec Turing (préprocesseur, métaprogrammation de modèles et C ++ de niveau de base)!

Pontus Gagge
la source
3
#define MY_MACRO_3(X,Y,Z) ...
#define MY_MACRO_2(X,Y) MY_MACRO(X,Y,5)
#define MY_MACRO_1(X) MY_MACRO(X,42,5)

Vous savez au moment de l'appel combien d'arguments vous allez transmettre, donc il n'y a vraiment pas besoin de surcharge.

Edward étrange
la source
2
Je demandais en fait l'existence de la fonctionnalité.
Cenoc
3

Version plus concise du code de Derek Ledbetter:

enum
{
    plain = 0,
    bold = 1,
    italic = 2
};


void PrintString(const char* message = NULL, int size = 0, int style = 0)
{
}


#define PRINT_STRING(...) PrintString(__VA_ARGS__)


int main(int argc, char * const argv[])
{ 
    PRINT_STRING("Hello, World!");
    PRINT_STRING("Hello, World!", 18);
    PRINT_STRING("Hello, World!", 18, bold);

    return 0;
}
Megamozg
la source
3

En tant que grand fan d'horribles monstres macro, je voulais développer la réponse de Jason Deng et la rendre réellement utilisable. (Pour le meilleur ou pour le pire.) L'original n'est pas très agréable à utiliser car vous devez modifier la grande soupe à l'alphabet à chaque fois que vous voulez faire une nouvelle macro et c'est encore pire si vous avez besoin d'un nombre d'arguments différent.

J'ai donc fait une version avec ces fonctionnalités:

  • 0 argument cas fonctionne
  • 1 à 16 arguments sans aucune modification de la partie désordonnée
  • Facile à écrire plus de fonctions macro
  • Testé dans gcc 10, clang 9, Visual Studio 2017

Actuellement, je viens de faire un maximum de 16 arguments, mais si vous avez besoin de plus (vraiment maintenant? Vous devenez simplement idiot ...) vous pouvez modifier FUNC_CHOOSER et CHOOSE_FROM_ARG_COUNT, puis ajouter des virgules à NO_ARG_EXPANDER.

Veuillez consulter l'excellente réponse de Jason Deng pour plus de détails sur l'implémentation, mais je vais simplement mettre le code ici:

#include <stdio.h>

void realCreate(int x, int y)
{
    printf("(%d, %d)\n", x, y);
}

// This part you put in some library header:
#define FUNC_CHOOSER(_f0, _f1, _f2, _f3, _f4, _f5, _f6, _f7, _f8, _f9, _f10, _f11, _f12, _f13, _f14, _f15, _f16, ...) _f16
#define FUNC_RECOMPOSER(argsWithParentheses) FUNC_CHOOSER argsWithParentheses
#define CHOOSE_FROM_ARG_COUNT(F, ...) FUNC_RECOMPOSER((__VA_ARGS__, \
            F##_16, F##_15, F##_14, F##_13, F##_12, F##_11, F##_10, F##_9, F##_8,\
            F##_7, F##_6, F##_5, F##_4, F##_3, F##_2, F##_1, ))
#define NO_ARG_EXPANDER(FUNC) ,,,,,,,,,,,,,,,,FUNC ## _0
#define MACRO_CHOOSER(FUNC, ...) CHOOSE_FROM_ARG_COUNT(FUNC, NO_ARG_EXPANDER __VA_ARGS__ (FUNC))
#define MULTI_MACRO(FUNC, ...) MACRO_CHOOSER(FUNC, __VA_ARGS__)(__VA_ARGS__)

// When you need to make a macro with default arguments, use this:
#define create(...) MULTI_MACRO(CREATE, __VA_ARGS__)
#define CREATE_0() CREATE_1(0)
#define CREATE_1(x) CREATE_2(x, 0)
#define CREATE_2(x, y) \
    do { \
        /* put whatever code you want in the last macro */ \
        realCreate(x, y); \
    } while(0)


int main()
{
    create();
    create(10);
    create(20, 20);
    //create(30, 30, 30);  // Compilation error
    return 0;
}
Kuukunen
la source
2

Vous pouvez utiliser à BOOST_PP_OVERLOADpartir d'une boostbibliothèque.

Exemple de la documentation officielle de boost :

#include <boost/preprocessor/facilities/overload.hpp>
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/facilities/empty.hpp>
#include <boost/preprocessor/arithmetic/add.hpp>

#define MACRO_1(number) MACRO_2(number,10)
#define MACRO_2(number1,number2) BOOST_PP_ADD(number1,number2)

#if !BOOST_PP_VARIADICS_MSVC

#define MACRO_ADD_NUMBERS(...) BOOST_PP_OVERLOAD(MACRO_,__VA_ARGS__)(__VA_ARGS__)

#else

// or for Visual C++

#define MACRO_ADD_NUMBERS(...) \
  BOOST_PP_CAT(BOOST_PP_OVERLOAD(MACRO_,__VA_ARGS__)(__VA_ARGS__),BOOST_PP_EMPTY())

#endif

MACRO_ADD_NUMBERS(5) // output is 15
MACRO_ADD_NUMBERS(3,6) // output is 9
désactiver13
la source
0

Selon ce dont vous avez besoin, vous pouvez le faire avec var args avec des macros. Maintenant, les paramètres optionnels ou la surcharge de macro, il n'y a rien de tel.

Gianni
la source
-1

Aucun des exemples ci-dessus (de Derek Ledbetter, David Sorkovsky et Joe D) pour compter les arguments avec des macros n'a fonctionné pour moi en utilisant Microsoft VCC 10. L' __VA_ARGS__argument est toujours considéré comme un argument unique (le token-izing avec ##ou pas), donc le changement d'argument dans lequel ces exemples reposent ne fonctionne pas.

Donc, réponse courte, comme indiqué par beaucoup d'autres ci-dessus: non, vous ne pouvez pas surcharger les macros ou utiliser des arguments optionnels dessus.

TheProgammerd
la source
1
Vous pouvez, mais uniquement en C99 ou C ++ 11 (en raison de la présence de __VA_ARGS__). VC2010 est C89 / C ++ 03 (avec quelques bits de C ++ 11 qui commencent à apparaître, mais pas encore celui-là).
puetzk