Spec-Zone.ru › GCC 10 CPP

Следующая: Предопределённые макросы, Предыдущая: Конкатенация, Вверх: Макросы [Оглавление][Индекс]

3.6 Переменные макросы ¶

Макрос может быть объявлен для приема переменного числа аргументов, аналогично функции. Синтаксис определения макроса похож на синтаксис функции. Вот пример:

#define eprintf(...) fprintf (stderr, __VA_ARGS__)

Этот тип макроса называется переменным. Когда макрос вызывается, все токены в списке его аргументов после последнего именованного аргумента (в этом макросе их нет), включая запятые, становятся переменным аргументом. Эта последовательность токенов заменяет идентификатор __VA_ARGS__ в теле макроса, где бы он ни появлялся. Таким образом, мы имеем такое развёртывание:

eprintf ("%s:%d: ", input_file, lineno)
     →  fprintf (stderr, "%s:%d: ", input_file, lineno)

Переменный аргумент полностью макро-расширяется перед его вставкой в расширение макроса, как и обычный аргумент. Вы можете использовать операторы ‘#’ и ‘##’ для стрингизации переменного аргумента или для склеивания его ведущего или хвостового токена с другим токеном. (Но см. специальный случай ниже для ‘##’.)

Если ваш макрос сложный, вы можете захотеть более описательное имя для переменного аргумента, чем __VA_ARGS__. CPP допускает это как расширение. Вы можете написать имя аргумента непосредственно перед ‘...’; это имя используется для переменного аргумента. Макрос eprintf выше можно переписать

#define eprintf(args...) fprintf (stderr, args)

используя это расширение. Вы не можете использовать __VA_ARGS__ и это расширение в одном макросе.

Вы можете иметь именованные аргументы, а также переменные аргументы в переменном макросе. Мы могли бы определить eprintf так:

#define eprintf(format, ...) fprintf (stderr, format, __VA_ARGS__)

Эта формулировка выглядит более описательно, но исторически она была менее гибкой: вам нужно было предоставить по крайней мере один аргумент после строки формата. В стандартном C вы не могли опустить запятую, разделяющую именованный аргумент и переменные аргументы. (Обратите внимание, что это ограничение было отменено в C++2a и никогда не существовало в GNU C; см. ниже.)

Кроме того, если бы вы оставили переменный аргумент пустым, вы получили бы синтаксическую ошибку, потому что после строки формата была бы лишняя запятая.

eprintf("success!\n", );
     → fprintf(stderr, "success!\n", );

Это было исправлено в C++2a, а GNU CPP также имеет пару расширений, которые решают эту проблему.

Во-первых, в GNU CPP и в C++ начиная с C++2a, вам разрешено полностью опустить переменный аргумент:

eprintf ("success!\n")
     → fprintf(stderr, "success!\n", );

Во-вторых, C++2a вводит функцию-макрос __VA_OPT__. Этот макрос может появляться только в определении переменного макроса. Если переменный аргумент имеет токены, то вызов __VA_OPT__ расширяется до своего аргумента; но если переменный аргумент не имеет токенов, то __VA_OPT__ расширяется до ничего:

#define eprintf(format, ...) \
  fprintf (stderr, format __VA_OPT__(,) __VA_ARGS__)

__VA_OPT__ также доступно в GNU C и GNU C++.

Исторически GNU CPP также имело другое расширение для обработки хвостовой запятой: оператор склейки токенов ‘##’ имеет особое значение, когда он помещен между запятой и переменным аргументом. Несмотря на введение __VA_OPT__, это расширение по-прежнему поддерживается в GNU CPP для обратной совместимости. Если вы напишите

#define eprintf(format, ...) fprintf (stderr, format, ##__VA_ARGS__)

и переменный аргумент опущен при использовании макроса eprintf, то запятая перед ‘##’ будет удалена. Это не происходит, если вы передаете пустой аргумент, и это не происходит, если токен, предшествующий ‘##’, является чем-то другим, кроме запятой.

eprintf ("success!\n")
     → fprintf(stderr, "success!\n");

Вышеприведённое объяснение неоднозначно в случае, когда единственный параметр макроса — это параметр переменных аргументов, так как бессмысленно пытаться различать, является ли отсутствие аргумента вообще пустым аргументом или отсутствующим аргументом. CPP сохраняет запятую при соответствии определённому стандарту C. В противном случае запятая опускается как расширение стандарта.

Стандарт C предписывает, что идентификатор __VA_ARGS__ может появляться только в списке подстановки переменного макроса. Он не может использоваться как имя макроса, имя аргумента макроса или внутри другого типа макроса. Он также может быть запрещён в открытом тексте; стандарт неоднозначен. Мы рекомендуем избегать его использования, за исключением определённой цели.

Аналогично, C++ запрещает __VA_OPT__ где угодно за пределами списка подстановки переменного макроса.

Переменные макросы стали стандартной частью языка C с C99. GNU CPP ранее поддерживал их с именованным переменным аргументом (‘args...’, а не ‘...’ и __VA_ARGS__), который по-прежнему поддерживается для обратной совместимости.

Следующая: Предопределённые макросы, Предыдущая: Конкатенация, Вверх: Макросы [Оглавление][Индекс]

© Free Software Foundation
Licensed under the GNU Free Documentation License, Version 1.3.
https://gcc.gnu.org/onlinedocs/gcc-10.5.0/cpp/Variadic-Macros.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API