Вариативный макрос - Variadic macro
А вариативный макрос это особенность некоторых компьютеров языки программирования, особенно Препроцессор C, посредством чего макрос может быть объявлено, что принимает различное количество аргументы.
Макросы с переменным аргументом были введены в 1999 году в ISO / IEC 9899: 1999 (C99 ) пересмотр C языкового стандарта, а в 2011 г. ИСО / МЭК 14882: 2011 (C ++ 11 ) пересмотр C ++ языковой стандарт.[1] Поддержка вариативных макросов без аргументов была добавлена в C ++ 20.[2]
Синтаксис объявления
Синтаксис объявления аналогичен синтаксису вариативные функции: последовательность из трех полная остановка "..."используется, чтобы указать, что необходимо передать один или несколько аргументов. Во время раскрытия макроса каждое вхождение специального идентификатора __VA_ARGS__ в списке замены макроса заменяется переданными аргументами.
Не предусмотрено никаких средств для доступа к отдельным аргументам в списке переменных аргументов или для определения количества переданных аргументов. Однако можно написать макросы для подсчета числа переданных аргументов.[3]
Оба C99 и C ++ 11 стандарты требуют хотя бы одного аргумента, но поскольку C ++ 20 это ограничение было снято __VA_OPT__ функциональный макрос. В __VA_OPT__ макрос заменяется своим аргументом, если присутствуют аргументы, и опускается в противном случае. Однако обычные компиляторы также позволяют передавать нулевые аргументы перед этим добавлением.[4][5]
Поддерживать
Несколько компиляторы поддержка макросов с переменными аргументами при компиляции кода C и C ++: Коллекция компиляторов GNU 3.0,[4] Лязг (все версии),[6] Visual Studio 2005,[5] C ++ Builder 2006 г. и Oracle Solaris Studio (ранее Sun Studio) Forte Developer 6, обновление 2 (C ++ версии 5.3).[7] GCC также поддерживает такие макросы при компиляции Цель-C.
Поддержка __VA_OPT__ макрос для поддержки нулевых аргументов был добавлен в Коллекция компиляторов GNU 8,[8] Лязг 6,[9] но особенно не Visual Studio 2017.[10]
Пример
Если printf
-подобно функция dbgprintf ()
были желательны, которые будут принимать в качестве аргументов файл и номер строки, из которой он был вызван, применяется следующее решение.
// Наша реализованная функцияпустота realdbgprintf (const char *SourceFilename, int ИсточникLineno, const char *CFormatString, ...);// Из-за ограничений поддержки вариативных макросов в C ++ 11 следующие// простое решение может потерпеть неудачу, поэтому его следует избегать://// #define dbgprintf (cformat, ...) // realdbgprintf (__FILE__, __LINE__, cformat, __VA_ARGS__)//// Причина в том, что//// dbgprintf ("Привет")//// расширяется до//// realdbgprintf (__FILE__, __LINE__, "Привет",)//// где запятая перед закрывающей фигурной скобкой приведет к синтаксической ошибке.//// 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 (__ФАЙЛ__, __ЛИНИЯ__, "Привет, мир");
Другой пример
dbgprintf("% d +% d =% d", 2, 2, 5);
который расширяется до
realdbgprintf(__ФАЙЛ__, __ЛИНИЯ__, "% d +% d =% d", 2, 2, 5);
Без вариативных макросов запись оберток в printf
напрямую невозможно. Стандартный обходной путь - использовать stdargs функциональность C / C ++ и иметь вызов функции vprintf
вместо.
Запятая в конце
Возникла проблема переносимости при создании конечной запятой с пустыми аргументами для макросов с переменным числом аргументов в C99. Некоторые компиляторы (например, Visual Studio[5]) автоматически удалит запятую. Другие компиляторы (например: GCC[4]) поддержка размещения ##
перед __VA_ARGS__.
# определить MYLOG (FormatLiteral, ...) fprintf (stderr, "% s (% u):" FormatLiteral " n", __FILE__, __LINE__, __VA_ARGS__)
Следующее приложение работает
MYLOG("Слишком много воздушных шаров% u", 42);
который расширяется до
fprintf (stderr, "% s (% u):" "Слишком много воздушных шаров% u" " п", __ФАЙЛ__, __ЛИНИЯ__, 42);
что эквивалентно
fprintf (stderr, "% s (% u): слишком много шариков% u п", __ФАЙЛ__, __ЛИНИЯ__, 42);
Но посмотрите на это приложение:
MYLOG("Внимание!");
который расширяется до
fprintf (stderr, "% s (% u):" "Внимание!" " п", __ФАЙЛ__, __ЛИНИЯ__, );
который генерирует синтаксическую ошибку с GCC.
GCC поддерживает следующее (непереносимое) расширение:
# определить MYLOG (FormatLiteral, ...) fprintf (stderr, "% s (% u):" FormatLiteral " n", __FILE__, __LINE__, ## __ VA_ARGS__)
который удаляет запятую, когда __VA_ARGS__ пусто.
Альтернативы
До появления переменных-аргументов в C99 было довольно распространено использовать двояко вложенные круглые скобки для использования переменного числа аргументов, которые могли быть переданы в printf ()
функция:
# определить dbgprintf (x) realdbgprintf x
dbgprintf ()
затем можно было бы назвать как:
dbgprintf (("Привет, мир% d", 27));
который расширяется до:
realdbgprintf ("Привет, мир% d", 27);
Рекомендации
- ^ Изменения рабочего проекта для синхронизации препроцессора C99 - http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1653.htm
- ^ «Пропуск запятой и удаление запятой». 12 июля 2017 г.. Получено 14 июня, 2018.
- ^ Лоран Денио (16 января 2006 г.). «__VA_NARG__». Группа новостей: comp.std.c. Usenet: [email protected].
- ^ а б c Макросы с переменными числами - Использование коллекции компиляторов GNU (GCC)
- ^ а б c Вариативные макросы (C ++)
- ^ Изменение исходного кода Clang, в котором упоминается поддержка __VA_ARGS__ (2006-07-29), обратите внимание, что исходный код Clang был открыт в 2007 году. http://llvm.org/viewvc/llvm-project?view=revision&revision=38770
- ^ Сравнение возможностей Sun Studio - http://developers.sun.com/sunstudio/support/CCcompare.html
- ^ «Поддержка C ++ 2a в GCC». Получено 14 июня, 2018.
- ^ «Поддержка C ++ в Clang». Получено 14 июня, 2018.
- ^ «Объявление: MSVC соответствует стандарту C ++». 7 мая 2018. Получено 14 июня, 2018.