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-8.5.0/cpp/Variadic-Macros.html