C'è un modo per ottenere parametri opzionali con le macro C ++? Anche una sorta di sovraccarico sarebbe bello.
No you can't
C'è un modo per ottenere parametri opzionali con le macro C ++? Anche una sorta di sovraccarico sarebbe bello.
No you can't
Risposte:
Ecco un modo per farlo. Utilizza l'elenco di argomenti due volte, prima per formare il nome della macro di supporto e quindi per passare gli argomenti a quella macro di supporto. Utilizza un trucco standard per contare il numero di argomenti in una 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;
}
Ciò semplifica il chiamante della macro, ma non l'autore.
PRINT_STRING_MACRO_CHOOSER
anche necessario? Posso sostituire direttamente con il suo corpo interno e chiamare tutta questa cosa con (__VA_ARGS__)
?
Con grande rispetto a Derek Ledbetter per la sua risposta e con scuse per aver ripreso una vecchia domanda.
Comprendere ciò che stava facendo e cogliere altrove la capacità di precedere il __VA_ARGS__
con ##
mi ha permesso di trovare una variazione ...
// 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__)\
)
Per i non esperti come me che si imbattono nella risposta, ma non riescono a vedere come funziona, passerò attraverso l'effettiva elaborazione, a partire dal seguente codice ...
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
Diventa ...
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) );
Che diventa solo il sesto argomento ...
XXX_0();
XXX_1(1);
XXX_2(1,2);
XXX_3(1,2,3);
XXX_4(1,2,3,4);
5;
PS: rimuovi #define per XXX_0 per ottenere un errore di compilazione [es .: se un'opzione senza argomenti non è consentita].
PPS: Sarebbe bello che le situazioni non valide (es .: 5) fossero qualcosa che fornisse un errore di compilazione più chiaro al programmatore!
PPPS: non sono un esperto, quindi sono molto felice di sentire commenti (buoni, cattivi o altro)!
XXX_X(,##__VA_ARGS__,` ...
XXX_X (, XXX_4 (), XXX_3 (), XXX_2 (), XXX_1 (), XXX_0 ()); `
Le macro C ++ non sono cambiate da C. Dal momento che C non aveva argomenti di sovraccarico e predefiniti per le funzioni, certamente non li aveva per le macro. Quindi, per rispondere alla tua domanda: no, queste funzionalità non esistono per le macro. L'unica opzione è definire più macro con nomi diversi (o non utilizzare affatto le macro).
Come nota a margine: in C ++ è generalmente considerata una buona pratica allontanarsi il più possibile dalle macro. Se hai bisogno di funzionalità come questa, ci sono buone probabilità che stai facendo un uso eccessivo delle macro.
__FILE__
e __LINE__
e tali ...
Con il massimo rispetto a Derek Ledbetter , David Sorkovsky , Syphorlate per le loro risposte, insieme al metodo ingegnoso per rilevare macro argomenti vuoti di Jens Gustedt a
https://gustedt.wordpress.com/2010/06/08/detect-empty-macro-arguments/
finalmente esco con qualcosa che incorpora tutti i trucchi, in modo che la soluzione
, ##__VA_ARGS__
per GCC / CLANG e deglutizione implicita da ##__VA_ARGS__
per MSVC). Quindi sentiti libero di passare ciò che manca --std=c99
al tuo compilatore se lo desideri =)Funziona ragionevolmente multipiattaforma , almeno testato per
Per i più pigri, vai all'ultimo di questo post per copiare la fonte. Di seguito è riportata la spiegazione dettagliata, che si spera aiuti e ispira tutte le persone che cercano le __VA_ARGS__
soluzioni generali come me. =)
Ecco come va. Prima definire la "funzione" user-visibile sovraccarico, ho chiamata create
, e la relativa definizione di funzione effettiva realCreate
, e le definizioni di macro con diverso numero di argomenti CREATE_2
, CREATE_1
, CREATE_0
, come mostrato di seguito:
#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__)
parte alla fine si risolve nei nomi delle definizioni delle macro e la seconda (__VA_ARGS__)
parte comprende i loro elenchi di parametri. Quindi la chiamata di un utente a si create(10)
risolve in CREATE_1(10)
, la CREATE_1
parte proviene da MACRO_CHOOSER(__VA_ARGS__)
e la (10)
parte proviene dalla seconda(__VA_ARGS__)
.
Il MACRO_CHOOSER
utilizza il trucco che, se __VA_ARGS__
è vuota, la seguente espressione viene concatenato in una chiamata macro valida dal preprocessore:
NO_ARG_EXPANDER __VA_ARGS__ () // simply shrinks to NO_ARG_EXPANDER()
Ingegnosamente, possiamo definire questa chiamata macro risultante come
#define NO_ARG_EXPANDER() ,,CREATE_0
Notare le due virgole, verranno presto spiegate. La prossima macro utile è
#define MACRO_CHOOSER(...) CHOOSE_FROM_ARG_COUNT(NO_ARG_EXPANDER __VA_ARGS__ ())
così le chiamate di
create();
create(10);
create(20, 20);
sono effettivamente espansi a
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);
Come suggerisce il nome della macro, dobbiamo contare il numero di argomenti in seguito. Ecco un altro trucco: il preprocessore fa solo una semplice sostituzione del testo. Deduce il numero di argomenti di una chiamata macro semplicemente dal numero di virgole che vede tra parentesi. Gli "argomenti" effettivi separati da virgole non devono necessariamente essere di sintassi valida. Possono essere qualsiasi testo. Vale a dire, nell'esempio sopra, NO_ARG_EXPANDER 10 ()
viene conteggiato come 1 argomento per la chiamata centrale. NO_ARG_EXPANDER 20
e20 ()
sono contati rispettivamente come 2 argomenti per l'ultima chiamata.
Se usiamo le seguenti macro di supporto per espanderle ulteriormente
##define CHOOSE_FROM_ARG_COUNT(...) \
FUNC_RECOMPOSER((__VA_ARGS__, CREATE_2, CREATE_1, ))
#define FUNC_RECOMPOSER(argsWithParentheses) \
FUNC_CHOOSER argsWithParentheses
Il trailing ,
after CREATE_1
è una soluzione per GCC / CLANG, sopprimendo un errore (falso positivo) che lo dice ISO C99 requires rest arguments to be used
quando si passa -pedantic
al compilatore. La FUNC_RECOMPOSER
è un modo per aggirare le MSVC, o non correttamente può contare il numero di argomenti (cioè virgole) all'interno delle parentesi chiamate macro. I risultati sono ulteriormente risolti
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);
Come gli occhi di aquila che potresti aver visto, l'ultimo unico passaggio di cui abbiamo bisogno è utilizzare un trucco standard per il conteggio degli argomenti per scegliere finalmente i nomi delle versioni macro desiderate:
#define FUNC_CHOOSER(_f1, _f2, _f3, ...) _f3
che risolve i risultati in
CREATE_0();
CREATE_1(10);
CREATE_2(20, 20);
e certamente ci dà le chiamate di funzione effettive desiderate:
realCreate(0, 0);
realCreate(10, 10);
realCreate(20, 20);
Mettendo tutto insieme, con qualche riorganizzazione delle affermazioni per una migliore leggibilità, l' intera fonte dell'esempio a 2 argomenti è qui:
#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;
}
Sebbene complicato, brutto, che appesantisca lo sviluppatore API, arriva una soluzione per sovraccaricare e impostare parametri opzionali delle funzioni C / C ++ per noi pazzi. L'utilizzo delle API sovraccariche in uscita diventa molto piacevole e piacevole. =)
Se è possibile semplificare ulteriormente questo approccio, fatemelo sapere all'indirizzo
https://github.com/jason-deng/C99FunctionOverload
Ancora un ringraziamento speciale a tutte le persone brillanti che mi hanno ispirato e portato a realizzare questo lavoro! =)
Per chiunque cerchi dolorosamente qualche soluzione VA_NARGS che funzioni con Visual C ++. La seguente macro ha funzionato perfettamente (anche con zero parametri!) In 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
Se vuoi una macro con parametri opzionali puoi fare:
//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__)))
Questo ha funzionato anche per me in vc. Ma non funziona per zero parametri.
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++
supporta le macro varargs ma non credo che questo sia standard, quindi usalo a tuo rischio.
#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 */
}
DISCLAIMER: Per lo più innocuo.
:%s/MY_MACRO_/THINK_/g
:)
i686-apple-darwin10-g++-4.2.1 (GCC) 4.2.1 (Apple Inc. build 5664)
Non è proprio ciò per cui è progettato il preprocessore.
Detto questo, se vuoi entrare nell'area della programmazione macro seriamente impegnativa con un minimo di leggibilità, dovresti dare un'occhiata alla libreria del preprocessore Boost . Dopotutto, non sarebbe C ++ se non ci fossero tre livelli di programmazione completamente compatibili con Turing (preprocessore, metaprogrammazione di template e C ++ di livello 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)
Sai al momento della chiamata quanti argomenti passerai, quindi non c'è davvero bisogno di sovraccarico.
Versione più concisa del codice di 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;
}
Essendo un grande fan degli orribili macro mostri, volevo espandere la risposta di Jason Deng e renderla effettivamente utilizzabile. (Nel bene o nel male.) L'originale non è molto bello da usare perché devi modificare la grande zuppa alfabetica ogni volta che vuoi creare una nuova macro ed è anche peggio se hai bisogno di una quantità diversa di argomenti.
Quindi ho realizzato una versione con queste caratteristiche:
Attualmente ho fatto solo 16 argomenti al massimo, ma se ne hai bisogno di più (davvero ora? Stai diventando sciocco ...) puoi modificare FUNC_CHOOSER e CHOOSE_FROM_ARG_COUNT, quindi aggiungere alcune virgole a NO_ARG_EXPANDER.
Si prega di consultare l'eccellente risposta di Jason Deng per maggiori dettagli sull'implementazione, ma inserirò il codice qui:
#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;
}
Puoi usare BOOST_PP_OVERLOAD
da una boost
libreria.
Esempio dal documento ufficiale di 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
Nessuno degli esempi precedenti (da Derek Ledbetter, David Sorkovsky e Joe D) per contare gli argomenti con le macro ha funzionato per me usando Microsoft VCC 10. L' __VA_ARGS__
argomento è sempre considerato come un singolo argomento (token-zzandolo con ##
o no), quindi lo spostamento dell'argomento su cui si basano questi esempi non funziona.
Quindi, risposta breve, come affermato da molti altri sopra: no, non puoi sovraccaricare le macro o utilizzare argomenti opzionali su di esse.