Расширение макроскопического расширения 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(); #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(); parent->GetProperty(); parent->GetProperty(); parent->GetProperty(); } Base * parent; } , struct MyStructure { void Foo() { parent->GetProperty(); parent->GetProperty(); parent->GetProperty(); parent->GetProperty(); } Base * parent; } , struct MyStructure { void Foo() { parent->GetProperty(); parent->GetProperty(); parent->GetProperty(); parent->GetProperty(); } Base * parent; } , struct MyStructure { void Foo() { parent->GetProperty(); parent->GetProperty(); parent->GetProperty(); parent->GetProperty(); } Base * parent; } , struct MyStructure { void Foo() { parent->GetProperty(); parent->GetProperty(); parent->GetProperty(); parent->GetProperty(); } Base * parent; } 

Но Microsoft почему-то расширяет все мои __VA_ARGS__ в качестве одного аргумента:

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

Кто-нибудь знает, почему это? Есть ли какой-то трюк, который я могу сделать, чтобы заставить Microsoft расширять это, как GCC? Может быть, бросить пару лишних пар круглых скобок?

Подобные macros могут помочь мне заменить кучу кода «клей», но из-за этой проблемы я не могу перенести ее в проект VS. Любая помощь будет принята с благодарностью!

Благодарю.

Кстати, мне довелось столкнуться с этой проблемой только сегодня, и после достаточного усилия я думаю, что нашел решение для своих целей. Ошибка заключается в том, что 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 

Но потом я подозреваю, что вы, вероятно, столкнетесь с проблемой обеспечения полного расширения до фактического «N», которое вы хотите, а не VA_NARGS_IMPL (arg1, arg2, 5, 4, 3, 2, 1) . Я обнаружил, что мой код (который похож на ваш) должен был меняться, чтобы расширять MAC##code как единое целое, а затем это должно было быть отдельно объединено с списком аргументов. Вот код, который я нашел, работал для меня:

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

Мне очень жаль, что через несколько часов я решил решить свои проблемы, чтобы потом полностью решить вашу проблему. 🙂 Но я думаю, этого достаточно, чтобы заставить вас работать над чем-то, с небольшим трудом.

Я знаю, что этот вопрос старше двух лет, но я думал, что попытаюсь дать более отполированный ответ тем, кто все еще спотыкается на это, как и я.

Ответ Джеффа Уолдена работает и все, но вы должны объявить FOO_CHOOSE_HELPER / 1/2 для каждого макроса FOO, который вы хотите иметь переменные аргументы. Я разработал слой абстракции, чтобы решить эту проблему. Рассмотрим следующее:

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

С помощью этой архитектуры вы можете определить переменные macros как таковые:

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

С ответом Джеффа вам нужно будет определить macros следующим образом:

 #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"); } 
  • Как установить путь в Visual Studio?
  • Самый точный способ выполнить комбинированную операцию умножения и деления в 64-битном режиме?
  • Почему некоторые символы Unicode приводят к отказу std :: wcout в консольном приложении?
  • Как использовать WinDbg для анализа дампа сбоя для приложения VC ++?
  • Win32: Верните окно вверх
  • ошибка LNK2019: нерешенный внешний символ _WinMain @ 16, указанный в функции ___tmainCRTStartup
  • CreateProcess из буфера памяти
  • что такое «выравнивание стека»?
  • Функция OpenSV SURF не реализована
  • Почему происходит сбой этой программы: передача std :: string между DLL
  • Почему фатальная ошибка «LNK1104: невозможно открыть файл« C: \ Program.obj »возникает при компиляции проекта C ++ в Visual Studio?
  • Interesting Posts

    Как разобрать stream ввода JSON

    Как избежать фигурных скобок для отображения на странице при использовании AngularJS?

    Устройства HomePlug на разных кольцевых цепях?

    Как разместить динамический компонент в контейнере

    Захват Node JS альтернатива многопоточности

    Могу ли я безопасно подключить только кабель питания USB-Y к другому источнику питания?

    Получите 3D-координаты из пикселя 2D-изображения, если известны внешние и внутренние параметры

    / Dev / fd0 в окнах. (Запись непосредственно на tar-дискету)

    Как фильтровать определенные приложения для намерения ACTION_SEND (и устанавливать другой текст для каждого приложения)

    Что более эффективно: Словарь TryGetValue или ContainsKey + Item?

    Требуется версия Gradle 2.2. Текущая версия 2.10

    Как написать символ и в файле android strings.xml

    Как получить и установить переменные среды в C #?

    Почему dd копирует только 128 байтов из / dev / random, когда я запрашиваю больше?

    Сколько примитивов требуется для создания LISP-машины? Десять, семь или пять?

    Давайте будем гением компьютера.