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