Расширение макроскопического расширения MSVC ++
Итак, у меня есть макрос, который хорошо работает в GCC, но не в компиляторе Microsoft C ++. Я надеюсь, что кто-то может знать об обходном пути, или, может быть, может объяснить мне, почему он ведет себя таким образом.
Я уверен, что этот макрос не совсем «стандартный», но это действительно поможет мне.
Вот функциональный пример макроса:
- Как читать с ресурса версии в Visual C ++
- Как компиляторы обрабатывают массивы переменной длины
- _addcarry_u64 и _addcarryx_u64 с MSVC и ICC
- memset () или инициализацию значения, чтобы обнулить структуру?
- Установка Visual Studio 2010 (любая версия) устанавливает только 2 файла в каталоге заголовков 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. Любая помощь будет принята с благодарностью!
Благодарю.
- Двойное удаление в файле initializer_list vs 2013
- GCC с Visual Studio?
- Ссылки DLL в Visual C ++
- Что такое Microsoft Visual Studio, эквивалентная опции GCC ld - whall-archive
- Зачем нужен двойной слой косвенности для макросов?
- Что такое простой прокси-сервер C ++ (VC ++)?
- Почему Visual Studio 2010 не может найти / открыть файлы PDB?
- C ++ Метод статического члена вызывает экземпляр classа
Кстати, мне довелось столкнуться с этой проблемой только сегодня, и после достаточного усилия я думаю, что нашел решение для своих целей. Ошибка заключается в том, что 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"); }