A вариационный макрос - это функция некоторых компьютерных языков программирования, особенно препроцессора C, посредством чего может быть объявлено, что макрос принимает различное количество аргументов..
Макросы с переменными аргументами были введены в 1999 году в версии ISO / IEC 9899: 1999 (C99 ) языкового стандарта C, и в 2011 году в версии ISO / IEC 14882: 2011 (C ++ 11 ) стандарта языка C ++. Поддержка вариативных макросов без аргументов была добавлена в C ++ 20.
Синтаксис объявления аналогичен синтаксису вариативных функций : последовательность из трех точек " ..."используется, чтобы указать, что необходимо передать один или несколько аргументов. Во время раскрытия макроса каждое появление специального идентификатора __VA_ARGS__в списке замены макроса заменяется переданными аргументами.
Не предусмотрено никаких средств для доступа к отдельным аргументам в списке переменных аргументов или для определения количества переданных аргументов. Однако можно написать макросы для подсчета числа переданных аргументов.
Оба стандарта C99 и C ++ 11 требуют по крайней мере одного аргумента, но начиная с C ++ 20 это ограничение было снято с помощью функционального макроса __VA_OPT__. Макрос __VA_OPT__заменяется своим аргументом, если присутствуют аргументы, и опускается в противном случае. Однако общие компиляторы также позволяют передавать нулевые аргументы перед этим добавлением.
Некоторые компиляторы поддерживают макросы с переменными аргументами при компиляции кода C и C ++: GNU Compiler Collection 3.0, Clang (все версии), Visual Studio 2005, C ++ Builder 2006 и Oracle Solaris Studio (ранее Sun Studio) Forte Developer 6, обновление 2 (C ++ версии 5.3). GCC также поддерживает такие макросы при компиляции Objective-C.
. Поддержка макроса __VA_OPT__для поддержки нулевых аргументов была добавлена в GNU Compiler Collection 8, Clang 6, но особенно не Visual Studio 2017.
Если printf
-подобная функция dbgprintf ()
, который принимает в качестве аргументов файл и номер строки, из которой он был вызван, применимо следующее решение.
// Наша реализованная функция void realdbgprintf (const char * SourceFilename, int SourceLineno, const char * CFormatString,...); // Из-за ограничений поддержки вариативных макросов в C ++ 11 следующее // простое решение может потерпеть неудачу, и его следует избегать: // // #define dbgprintf (cformat,...) \ // realdbgprintf (__FILE__, __LINE__, cformat, __VA_ARGS__) // // Причина в том, что // // dbgprintf ("Hallo") // // расширяется до // // realdbgprintf (__FILE__, __LINE__, "Hallo",) // // где запятая перед закрывающей фигурной скобкой приведет к синтаксической ошибке. // // GNU C ++ поддерживает непереносимое расширение, которое решает эту проблему. // // #define dbgprintf (cformat,...) \ // realdbgprintf (__FILE__, __LINE__, cformat, ## __ VA_ARGS__) // // C ++ 20 в конечном итоге поддерживает следующий синтаксис. // // #define dbgprintf (cformat,...) \ // realdbgprintf (__FILE__, __LINE__, cformat __VA_OPT __ (,) __VA_ARGS__) // // Используя строку 'cformat' как часть аргументов с переменным числом аргументов, мы можем / / обойти вышеупомянутые несовместимости. Это сложно, но // переносимо. #define dbgprintf (...) realdbgprintf (__FILE__, __LINE__, __VA_ARGS__)
dbgprintf ()
затем может вызываться как
dbgprintf ("Привет, мир");
, который заменяется на
realdbgprintf (__FILE__, __LINE__, «Привет, мир»);
Другой пример:
dbgprintf ("% d +% d =% d", 2, 2, 5);
, который расширяется до
realdbgprintf (__ FILE__, __LINE__, "% d +% d =% d", 2, 2, 5);
Без вариативных макросов запись оболочки в printf
напрямую невозможна. Стандартный обходной путь - использовать функциональные возможности C / C ++ stdargs и вместо этого использовать вызов функции vprintf
.
Возникла проблема переносимости при создании конечной запятой с пустыми аргументами для макросов с переменным числом аргументов в C99. Некоторые компиляторы (например, Visual Studio) автоматически удаляют запятую. Другие компиляторы (например: GCC) поддерживают размещение ##
перед __VA_ARGS__.
# define MYLOG (FormatLiteral,...) fprintf (stderr, "% s (% u):" FormatLiteral "\ n", __FILE__, __LINE__, __VA_ARGS__)
Следующее приложение работает
MYLOG («Слишком много баллов% u», 42);
, который заменяется на
fprintf (stderr, "% s (% u):" "Слишком много всплывающих подсказок% u" "\ n", __FILE__, __LINE__, 42);
, что эквивалентно
fprintf (stderr, "% s (% u): слишком много баллов% u \ n", __FILE__, __LINE__, 42);
Но посмотрите на это приложение:
MYLOG («Внимание!»);
, который расширяется до
fprintf (stderr, "% s (% u):" "Внимание!" "\ N", __FILE__, __LINE__,);
, который генерирует синтаксическую ошибку с GCC.
GCC поддерживает следующее (непереносимое) расширение:
# define MYLOG (FormatLiteral,...) fprintf (stderr, "% s (% u):" FormatLiteral "\ n", __FILE__, __LINE__, ## __ VA_ARGS__)
который удаляет конечную запятую, когда __VA_ARGS__пусто.
До появления переменных-аргументов в C99 было довольно распространено использовать двояко вложенные круглые скобки для использования переменного числа аргументов, которые могли быть переданы в printf ()
function:
# define dbgprintf (x) realdbgprintf x
dbgprintf ()
затем может вызываться как:
dbgprintf (("Привет, мир% d", 27));
, который расширяется до:
realdbgprintf ("Привет, мир% d", 27);