Вариативный макрос - 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);

Рекомендации

  1. ^ Изменения рабочего проекта для синхронизации препроцессора C99 - http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1653.htm
  2. ^ «Пропуск запятой и удаление запятой». 12 июля 2017 г.. Получено 14 июня, 2018.
  3. ^ Лоран Денио (16 января 2006 г.). «__VA_NARG__». Группа новостейcomp.std.c. Usenet:  [email protected].
  4. ^ а б c Макросы с переменными числами - Использование коллекции компиляторов GNU (GCC)
  5. ^ а б c Вариативные макросы (C ++)
  6. ^ Изменение исходного кода Clang, в котором упоминается поддержка __VA_ARGS__ (2006-07-29), обратите внимание, что исходный код Clang был открыт в 2007 году. http://llvm.org/viewvc/llvm-project?view=revision&revision=38770
  7. ^ Сравнение возможностей Sun Studio - http://developers.sun.com/sunstudio/support/CCcompare.html
  8. ^ «Поддержка C ++ 2a в GCC». Получено 14 июня, 2018.
  9. ^ «Поддержка C ++ в Clang». Получено 14 июня, 2018.
  10. ^ «Объявление: MSVC соответствует стандарту C ++». 7 мая 2018. Получено 14 июня, 2018.

Смотрите также