Spec-Zone.ru › GCC 6 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 вы не можете опустить запятую, разделяющую именованный аргумент и переменные аргументы. Кроме того, если вы оставите переменный аргумент пустым, вы получите синтаксическую ошибку, потому что после строки формата будет лишняя запятая.

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

Spec-Zone.ru

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