Spec-Zone.ru › GCC 6 CPP

3.10.6 Предварительный анализ аргументов

Аргументы макроса полностью макрорасширяются перед их подстановкой в тело макроса, если они не строятся или не склеиваются с другими токенами. После подстановки всё тело макроса, включая подставленные аргументы, снова анализируется на предмет макросов для расширения. В результате аргументы анализируются дважды, чтобы расширить вызовы макросов в них.

В большинстве случаев это не имеет эффекта. Если аргумент содержал вызовы макросов, они расширяются во время первого сканирования. В результате он не содержит вызовов макросов, поэтому второе сканирование его не изменяет. Если аргумент был подставлен как есть без предварительного анализа, единственное оставшееся сканирование обнаружило бы те же вызовы макросов и дало бы те же результаты.

Можно ожидать, что двойное сканирование изменит результаты при использовании рекурсивного макроса в аргументе другого макроса (см. Рекурсивные макросы): рекурсивный макрос расширится один раз в первом сканировании и второй раз во втором сканировании. Однако этого не происходит. Ссылки на себя, которые не расширяются в первом сканировании, помечаются таким образом, что они не будут расширяться и во втором сканировании.

Можно задаться вопросом: «Зачем упоминать предварительный анализ, если он ничего не меняет? И почему не пропустить его и сделать препроцессор быстрее?» Ответ заключается в том, что предварительный анализ влияет в трёх особых случаях:

  • Вложенные вызовы макроса.

    Мы говорим, что вложенные вызовы макроса происходят, когда аргумент макроса содержит вызов этого же макроса. Например, если f — это макрос, который ожидает один аргумент, f (f (1)) — это вложенная пара вызовов f. Желаемое расширение выполняется путём расширения f (1) и подстановки его в определение f. Предварительный анализ приводит к ожидаемому результату. Без предварительного анализа f (1) сам был бы подставлен как аргумент, а внутреннее использование f появилось бы во время основного сканирования как косвенная ссылка на себя и не было бы расширено.

  • Макросы, которые вызывают другие макросы, которые строят или конкатенируют.

    Если аргумент строит или конкатенирует, предварительный анализ не выполняется. Если вы хотите расширить макрос, затем построить или склеить его расширение, вы можете сделать это, заставив один макрос вызвать другой макрос, который выполняет строку или конкатенацию. Например, если у вас есть

    #define AFTERX(x) X_ ## x
    #define XAFTERX(x) AFTERX(x)
    #define TABLESIZE 1024
    #define BUFSIZE TABLESIZE

    тогда AFTERX(BUFSIZE) расширяется до X_BUFSIZE, а XAFTERX(BUFSIZE) расширяется до X_1024. (Не до X_TABLESIZE. Предварительный анализ всегда выполняет полное расширение.)

  • Макросы, используемые в аргументах, чьи расширения содержат незащищённые запятые.

    Это может привести к вызову макроса, расширенного во втором сканировании, с неправильным количеством аргументов. Вот пример:

    #define foo  a,b
    #define bar(x) lose(x)
    #define lose(x) (1 + (x))

    Мы хотели бы, чтобы bar(foo) превратилось в (1 + (foo)), которое затем превратилось бы в (1 + (a,b)). Вместо этого bar(foo) расширяется до lose(a,b), и вы получите ошибку, потому что lose требует одного аргумента. В этом случае проблема легко решается теми же скобками, которые должны использоваться для предотвращения неправильного вложения арифметических операций:

    #define foo (a,b)
    or
    #define bar(x) lose((x))

    Дополнительная пара скобок предотвращает интерпретацию запятой в определении foo как разделителя аргументов.

Далее: Перевод строки в аргументах, Предыдущее: Рекурсивные макросы, Вверх: Ловушки макросов [Оглавление][Индекс]

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

Spec-Zone.ru

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