Espansione macro variadica MSVC ++

Quindi ho una macro che funziona bene in GCC, ma non nel compilatore C ++ di Microsoft. Spero che qualcuno possa sapere di una soluzione alternativa, o forse può spiegarmi perché si comporta in questo modo.

Sono sicuro che questa macro non è esattamente “standard”, ma mi aiuterebbe davvero.

Ecco un esempio funzionale della macro:

#define VA_NARGS_IMPL(_1, _2, _3, _4, _5, N, ...) N #define VA_NARGS(...) VA_NARGS_IMPL(__VA_ARGS__, 5, 4, 3, 2, 1) #define FULLY_EXPANDED(count, ...) \ MAC ## count (__VA_ARGS__) #define SEMI_EXPANDED(count, ...) FULLY_EXPANDED(count, __VA_ARGS__) #define EXPAND_THESE(...) SEMI_EXPANDED(VA_NARGS(__VA_ARGS__), __VA_ARGS__) #define ACTUAL_MACRO(x) parent->GetProperty(); #define MAC1(a) ACTUAL_MACRO(a) #define MAC2(a,b) MAC1(a) ACTUAL_MACRO(b) #define MAC3(a,b,c) MAC2(a,b) ACTUAL_MACRO(c) #define MAC4(a,b,c,d) MAC3(a,b,c) ACTUAL_MACRO(d) #define MAC5(a,b,c,d,e) MAC4(a,b,c,d) ACTUAL_MACRO(e) 

Ecco come potrei usare questa macro:

 struct MyStructure { void Foo() { EXPAND_THESE(Property1, Property2, Property3, Property4) } Base * parent; } 

Ecco come GCC espande quanto sopra:

 struct MyStructure { void Foo() { parent->GetProperty(); parent->GetProperty(); parent->GetProperty(); parent->GetProperty(); } Base * parent; } 

Ma Microsoft per qualche ragione espande tutto il mio __VA_ARGS__ come un argomento:

 struct MyStructure { void Foo() { parent->GetProperty(); } Base * parent; } 

Qualcuno sa perché questo è? C’è qualche trucco che posso tirare per far espandere Microsoft come GCC? Forse aggiungere un paio di coppie in più di parentesi?

Macro come questa potrebbero davvero aiutarmi a sostituire un mucchio di codice “colla”, ma a causa di questo problema, non posso spostarlo nel mio progetto VS. Qualsiasi aiuto sarebbe molto apprezzato!

Grazie.

Per coincidenza, mi è capitato di imbattersi in questo problema proprio oggi, e dopo un certo sforzo penso di aver trovato una soluzione per i miei scopi. Il bug è che MSVC tratta __VA_ARGS__ come un singolo token negli elenchi di argomenti. Ma puoi aggirare questo problema non usando direttamente all’interno di un elenco di argomenti di chiamata macro. Questo commento suggerisce l’inizio di una risposta ai tuoi problemi:

 #define VA_NARGS(...) VA_NUM_ARGS_IMPL_((__VA_ARGS__, 5,4,3,2,1)) #define VA_NARGS_IMPL_(tuple) VA_NUM_ARGS_IMPL tuple #define VA_NARGS_IMPL(_1,_2,_3,_4,_5,N,...) N 

Ma sospetto che probabilmente ti imbatterai nel problema di assicurarti che si VA_NARGS_IMPL (arg1, arg2, 5, 4, 3, 2, 1) completamente alla “N” effettiva che vuoi, e non a VA_NARGS_IMPL (arg1, arg2, 5, 4, 3, 2, 1) , per esempio. Ho scoperto che il mio codice (che sembrava il tuo) doveva cambiare per espandere il MAC##code come un’unica unità, e quindi doveva essere combinato separatamente con la lista degli argomenti. Ecco il codice che ho trovato ha funzionato per me:

 #define ASSERT_HELPER1(expr) singleArgumentExpansion(expr) #define ASSERT_HELPER2(expr, explain) \ twoArgumentExpansion(expr, explain) /* * Count the number of arguments passed to ASSERT, very carefully * tiptoeing around an MSVC bug where it improperly expands __VA_ARGS__ as a * single token in argument lists. See these URLs for details: * * http://connect.microsoft.com/VisualStudio/feedback/details/380090/variadic-macro-replacement * http://cplusplus.co.il/2010/07/17/variadic-macro-to-count-number-of-arguments/#comment-644 */ #define COUNT_ASSERT_ARGS_IMPL2(_1, _2, count, ...) \ count #define COUNT_ASSERT_ARGS_IMPL(args) \ COUNT_ASSERT_ARGS_IMPL2 args #define COUNT_ASSERT_ARGS(...) \ COUNT_ASSERT_ARGS_IMPL((__VA_ARGS__, 2, 1, 0)) /* Pick the right helper macro to invoke. */ #define ASSERT_CHOOSE_HELPER2(count) ASSERT_HELPER##count #define ASSERT_CHOOSE_HELPER1(count) ASSERT_CHOOSE_HELPER2(count) #define ASSERT_CHOOSE_HELPER(count) ASSERT_CHOOSE_HELPER1(count) /* The actual macro. */ #define ASSERT_GLUE(x, y) xy #define ASSERT(...) \ ASSERT_GLUE(ASSERT_CHOOSE_HELPER(COUNT_ASSERT_ARGS(__VA_ARGS__)), \ (__VA_ARGS__)) int foo() { ASSERT(one); // singleArgumentExpansion(one) ASSERT(two, "foopy"); // twoArgumentExpansion(two, "foopy") } 

La mia mente è troppo fitta dopo poche ore a risolvere i miei problemi per poi andare a risolvere completamente la tua, mi dispiace dirlo. 🙂 Ma penso che sia abbastanza per farti fare qualcosa che funziona, con un po ‘di lavoro.

So che questa domanda ha più di due anni, ma ho pensato di provare a dare una risposta più lucida a coloro che ancora incappano in questo, come ho fatto io.

La risposta di Jeff Walden funziona e tutto, ma devi dichiarare FOO_CHOOSE_HELPER / 1/2 per ogni macro FOO che vuoi avere argomenti variadici. Ho sviluppato uno strato di astrazione per risolvere questo problema. Considera quanto segue:

 #define GLUE(x, y) xy #define RETURN_ARG_COUNT(_1_, _2_, _3_, _4_, _5_, count, ...) count #define EXPAND_ARGS(args) RETURN_ARG_COUNT args #define COUNT_ARGS_MAX5(...) EXPAND_ARGS((__VA_ARGS__, 5, 4, 3, 2, 1, 0)) #define OVERLOAD_MACRO2(name, count) name##count #define OVERLOAD_MACRO1(name, count) OVERLOAD_MACRO2(name, count) #define OVERLOAD_MACRO(name, count) OVERLOAD_MACRO1(name, count) #define CALL_OVERLOAD(name, ...) GLUE(OVERLOAD_MACRO(name, COUNT_ARGS_MAX5(__VA_ARGS__)), (__VA_ARGS__)) 

Con questa architettura è ansible definire macro variadici come tali:

 #define ERROR1(title) printf("Error: %s\n", title) #define ERROR2(title, message)\ ERROR1(title);\ printf("Message: %s\n", message) #define ERROR(...) CALL_OVERLOAD(ERROR, __VA_ARGS__) #define ASSERT1(expr) singleArgumentExpansion(expr) #define ASSERT2(expr, explain) twoArgumentExpansion(expr, explain) #define ASSERT(...) CALL_OVERLOAD(ASSERT, __VA_ARGS__) 

Con la risposta di Jeff dovresti definire i macro come segue:

 #define ERROR1(title) printf("Error: %s\n", title) #define ERROR2(title, message)\ ERROR1(title);\ printf("Message: %s\n", message) #define ERROR_CHOOSE_HELPER2(count) ERROR##count #define ERROR_CHOOSE_HELPER1(count) ERROR_CHOOSE_HELPER2(count) #define ERROR_CHOOSE_HELPER(count) ERROR_CHOOSE_HELPER1(count) #define ERROR(...) GLUE(ERROR_CHOOSE_HELPER(COUNT_ARGS_MAX5(__VA_ARGS__)),\ (__VA_ARGS__)) #define ASSERT1(expr) singleArgumentExpansion(expr) #define ASSERT2(expr, explain) twoArgumentExpansion(expr, explain) #define ASSERT_CHOOSE_HELPER2(count) ASSERT##count #define ASSERT_CHOOSE_HELPER1(count) ASSERT_CHOOSE_HELPER2(count) #define ASSERT_CHOOSE_HELPER(count) ASSERT_CHOOSE_HELPER1(count) #define ASSERT(...) GLUE(ASSERT_CHOOSE_HELPER(COUNT_ARGS_MAX5(__VA_ARGS__)),\ (__VA_ARGS__)) 

Non è un grosso problema, tuttavia mi piace che il mio codice sia il più conciso ansible. Aiuta anche in modo esponenziale, se stai usando diverse macro variadic, per ridurre la duplicazione del codice e le complicazioni che possono causare. Per quanto ne so, questo metodo è anche portatile. L’ho testato su molti dei compilatori più comuni e hanno prodotto gli stessi risultati.

Esempio di utilizzo:

 int foo() { ASSERT(one); // singleArgumentExpansion(one) ASSERT(two, "foopy"); // twoArgumentExpansion(two, "foopy") ERROR("Only print a title"); ERROR("Error title", "Extended error description"); }