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 вы не можете опустить запятую, разделяющую именованный аргумент и переменные аргументы. Кроме того, если вы оставите переменный аргумент пустым, вы получите синтаксическую ошибку, потому что после строки форматирования будет дополнительная запятая.
eprintf("success!\n", );
→ fprintf(stderr, "success!\n", );GNU CPP имеет пару расширений, которые решают эту проблему. Во-первых, вам разрешается вообще опустить переменный аргумент:
eprintf ("success!\n")
→ fprintf(stderr, "success!\n", );Во-вторых, оператор склеивания токенов ‘##’ имеет специальное значение, когда он помещается между запятой и переменным аргументом. Если вы напишете
#define eprintf(format, …) fprintf (stderr, format, ##__VA_ARGS__)
и переменный аргумент опущен при использовании макроса eprintf, тогда запятая перед ‘##’ будет удалена. Это не происходит, если вы передаёте пустой аргумент, и это не происходит, если токен, предшествующий ‘##’, является чем-то другим, кроме запятой.
eprintf ("success!\n")
→ fprintf(stderr, "success!\n");Вышеизложенное объяснение неоднозначно в случае, когда единственным параметром макроса является параметр переменных аргументов, так как бессмысленно пытаться отличить, является ли отсутствие аргументов пустым аргументом или отсутствующим аргументом. В этом случае стандарт C99 чётко указывает, что запятая должна остаться, однако существующее расширение GCC использовалось для проглатывания запятой. Поэтому CPP сохраняет запятую при соответствии определённому стандарту C и опускает её в противном случае.
C99 предписывает, что идентификатор __VA_ARGS__ может появляться только в списке замены макроса с переменным числом аргументов. Его нельзя использовать в качестве имени макроса, имени аргумента макроса или внутри макроса другого типа. Он также может быть запрещён в открытом тексте; стандарт неоднозначен. Мы рекомендуем избегать его использования, кроме его определённого назначения.
Макросы с переменным числом аргументов являются новой функцией в C99. GNU CPP поддерживает их уже давно, но только с именованным переменным аргументом (‘args…’, а не ‘…’ и __VA_ARGS__). Если вы обеспокоены переносимостью на предыдущие версии GCC, вы должны использовать только именованные переменные аргументы. С другой стороны, если вы обеспокоены переносимостью на другие соответствующие реализации C99, вы должны использовать только __VA_ARGS__.
Предыдущие версии CPP реализовывали расширение удаления запятой гораздо более широко. Мы ограничили его в этом выпуске, чтобы свести к минимуму различия со стандартом C99. Чтобы получить тот же эффект как в этой, так и в предыдущих версиях GCC, токен, предшествующий специальному ‘##’, должен быть запятой, и между этой запятой и тем, что непосредственно перед ней, должно быть пробельное пространство:
#define eprintf(format, args…) fprintf (stderr, format , ##args)
См. Различия с предыдущими версиями, для подробностей.
Далее: Предопределённые макросы, Предыдущий: Конкатенация, Вверх: Макросы [Оглавление][Индекс]
© Free Software Foundation
Licensed under the GNU Free Documentation License, Version 1.3.
https://gcc.gnu.org/onlinedocs/gcc-5.5.0/cpp/Variadic-Macros.html