MSVC++ вариадическое расширение макросов
Итак, у меня есть макрос, который прекрасно работает в GCC, но не в компиляторе Microsoft C++. Я надеюсь, что кто-то может знать о обходном пути, или, возможно, может объяснить мне, почему он ведет себя таким образом.
Я уверен, что этот макрос не совсем "стандартный", но он действительно поможет мне.Вот функциональный пример макроса:
#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)
Вот как я мог бы использовать этот макрос:
struct MyStructure
{
void Foo()
{
EXPAND_THESE(Property1, Property2, Property3, Property4)
}
Base * parent;
}
Вот как GCC расширяет вышесказанное:
struct MyStructure
{
void Foo()
{
parent->GetProperty<Property1>();
parent->GetProperty<Property2>();
parent->GetProperty<Property3>();
parent->GetProperty<Property4>();
}
Base * parent;
}
Но Microsoft по какой-то причине расширяет все мои __ва_параметры__ как один из аргументов:
struct MyStructure
{
void Foo()
{
parent->GetProperty<Property1, Property2, Property3, Property4>();
}
Base * parent;
}
Кто-нибудь знает, почему это так? Есть ли какой-то трюк, который я могу использовать, чтобы заставить Microsoft расширить это, как GCC? Может быть, добавить пару дополнительных пар скобок?
Макросы, подобные этому, действительно могли бы помочь мне заменить кучу "клеевого" кода, но из-за этой проблемы я не могу переместить его в свой проект VS. Любая помощь будет очень признательна!
Спасибо.
2 ответа:
По случайному совпадению, я столкнулся с этой проблемой только сегодня, и после достаточных усилий я думаю, что нашел решение для своих собственных целей. Ошибка заключается в том, что MSVC обрабатывает
__VA_ARGS__
как один маркер в списках аргументов. Но вы можете обойти это, не используя его непосредственно в списке аргументов вызова макросов. этот комментарий предполагает начало ответа на ваши проблемы:#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
Но тогда я подозреваю, что вы, вероятно, столкнетесь с проблемой обеспечения того, чтобы это было полностью расширено до собственно "Н" вы хотите, а не
VA_NARGS_IMPL (arg1, arg2, 5, 4, 3, 2, 1)
, скажем. Я обнаружил, что мой код (который выглядел как ваш) должен был измениться, чтобы развернутьMAC##code
все как один блок, а затем это должно было быть отдельно объединено со списком аргументов. Вот код, который я нашел, работал для меня:[5] мой разум слишком сильно помутился после нескольких часов решения моих собственных проблем, чтобы затем пойти и полностью решить ваши, Я сожалею об этом. :- ) Но я думаю, что этого достаточно, чтобы заставить вас сделать что-то, что работает, с небольшой работой.#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") }
Я знаю, что этому вопросу уже больше двух лет, но я подумал, что постараюсь дать более точный ответ тем, кто все еще натыкается на это, как и я.
Ответ Джеффа Уолдена работает и все такое, но вы должны объявить FOO_CHOOSE_HELPER/1 / 2 для каждого макроса FOO, который вы хотите иметь вариадические аргументы. Я разработал слой абстракции, чтобы решить эту проблему. Рассмотрим следующее:#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__))
С помощью этой архитектуры можно определить вариадические макросы как таковые:
#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__)
С Ответ Джеффа вы должны были бы определить макросы следующим образом:
#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__))
Это не так уж и важно, однако мне нравится, чтобы мой код был максимально лаконичным. Это также помогает экспоненциально, если вы используете несколько вариадических макросов, уменьшить дублирование кода и осложнения, которые могут возникнуть. Насколько я знаю, этот метод также переносим. Я протестировал его на многих наиболее распространенных компиляторах, и они дали те же результаты.
Пример использования:
int foo() { ASSERT(one); // singleArgumentExpansion(one) ASSERT(two, "foopy"); // twoArgumentExpansion(two, "foopy") ERROR("Only print a title"); ERROR("Error title", "Extended error description"); }