c++ visual-c++ c-preprocessor variadic-macros

Expansión macro macro variada MSVC++



visual-c++ c-preprocessor (2)

Casualmente, me encontré con este problema solo hoy, y después de suficiente esfuerzo creo que he encontrado una solución para mis propios fines. El error es que __VA_ARGS__ trata a __VA_ARGS__ como un único token en las listas de argumentos. Pero puede evitar esto al no usarlo directamente dentro de una lista de argumentos de macro llamada. Este comentario sugiere el comienzo de una respuesta a sus problemas:

#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

Pero sospecho que probablemente se encontrará con la cuestión de asegurarse de que se expanda completamente a la "N" real que desea, y no a VA_NARGS_IMPL (arg1, arg2, 5, 4, 3, 2, 1) , por ejemplo. Descubrí que mi código (que se parecía al suyo) tuvo que cambiar para expandir MAC##code como una sola unidad, y luego tenía que combinarse por separado con la lista de argumentos. Aquí está el código que encontré funcionó para mí:

#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) x y #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") }

Mi mente está demasiado agitada después de unas horas resolviendo mis propios problemas para luego ir y resolver completamente la tuya, lamento decirlo. :-) Pero creo que esto es suficiente para que hagas algo que funcione, con un poco de trabajo.

Así que tengo una macro que funciona bien en GCC, pero no en el compilador C ++ de Microsoft. Espero que alguien sepa de una solución, o quizás pueda explicarme por qué se comporta de esta manera.

Estoy seguro de que esta macro no es exactamente "estándar", pero realmente me ayudaría.

Aquí hay un ejemplo funcional de la 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<x>(); #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)

Aquí es cómo podría usar esta macro:

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

Así es como GCC expande lo anterior:

struct MyStructure { void Foo() { parent->GetProperty<Property1>(); parent->GetProperty<Property2>(); parent->GetProperty<Property3>(); parent->GetProperty<Property4>(); } Base * parent; }

Pero Microsoft por alguna razón expande todos mis __VA_ARGS__ como un argumento:

struct MyStructure { void Foo() { parent->GetProperty<Property1, Property2, Property3, Property4>(); } Base * parent; }

Alguien sabe por qué es esto? ¿Hay algún truco que pueda utilizar para que Microsoft expanda esto como GCC? ¿Tal vez tirar un par de paréntesis adicionales?

Las macros como esta podrían realmente ayudarme a reemplazar un montón de código "pegamento", pero debido a este problema, no puedo moverlo a mi proyecto VS. ¡Cualquier ayuda sería muy apreciada!

Gracias.


Sé que esta pregunta tiene más de dos años, pero pensé que trataría de dar una respuesta más refinada a aquellos que todavía tropiezan con esto, como yo.

La respuesta de Jeff Walden funciona y todo, pero debes declarar FOO_CHOOSE_HELPER / 1/2 para cada macro de FOO que desees tener argumentos variados. He desarrollado una capa de abstracción para resolver este problema. Considera lo siguiente:

#define GLUE(x, y) x y #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 esta arquitectura puede definir macros variadas como tales:

#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 respuesta de Jeff, debería definir las macros de la siguiente manera:

#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__))

No es gran cosa, sin embargo, me gusta que mi código sea lo más conciso posible. También ayuda de forma exponencial, si está utilizando varias macros variadas, para reducir la duplicación de código y las complicaciones que pueden causar. Hasta donde yo sé, este método también es portátil. Lo probé en muchos de los compiladores más comunes y produjeron los mismos resultados.

Ejemplo de uso:

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