Existe-t-il un moyen d'obtenir des paramètres facultatifs avec les macros C ++? Une sorte de surcharge serait bien aussi.
No you can't
Existe-t-il un moyen d'obtenir des paramètres facultatifs avec les macros C ++? Une sorte de surcharge serait bien aussi.
No you can't
Réponses:
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.
PRINT_STRING_MACRO_CHOOSER
même nécessaire? Puis-je remplacer directement par son corps intérieur et appeler tout cela avec (__VA_ARGS__)
?
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)!
XXX_X(,##__VA_ARGS__,` ...
XXX_X (, XXX_4 (), XXX_3 (), XXX_2 (), XXX_1 (), XXX_0 ()); `
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.
__FILE__
et __LINE__
et autres ...
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
, ##__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 =)Fonctionne raisonnablement multiplateforme , au moins testé pour
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_0
comme 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_1
pièce vient de MACRO_CHOOSER(__VA_ARGS__)
, et la (10)
pièce vient de la seconde (__VA_ARGS__)
.
Le MACRO_CHOOSER
utilise 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 20
et 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_1
est une solution de contournement pour GCC / CLANG, supprimant une erreur (faux positif) indiquant cela ISO C99 requires rest arguments to be used
lors du passage -pedantic
à votre compilateur. Il FUNC_RECOMPOSER
s'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! =)
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
unresolved external symbol _bool referenced in function _main
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.
#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.
:%s/MY_MACRO_/THINK_/g
:)
i686-apple-darwin10-g++-4.2.1 (GCC) 4.2.1 (Apple Inc. build 5664)
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)!
#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.
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;
}
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:
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;
}
Vous pouvez utiliser à BOOST_PP_OVERLOAD
partir d'une boost
bibliothè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
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.