Когда макрос найден, препроцессор собирает аргументы для макроса и затем сканирует каждый аргумент макроса изолированно, чтобы другие макросы раскрылись в аргументе ДО того, как развернется первый макрос:
6.10.3.1 Подстановка аргумента
После того, как аргументы для вызова функционально-подобного макроса были определены, происходит подстановка аргумента.Параметр в списке замены, за исключением случаев, когда перед ним стоит токен предварительной обработки # или ## или после него идет токен предварительной обработки ## (см. Ниже), заменяется соответствующим аргументом после раскрытия всех содержащихся в нем макросов.Перед заменой токены предварительной обработки каждого аргумента полностью заменяются макросами, как если бы они формировали остальную часть файла предварительной обработки;другие токены предварительной обработки недоступны.
Так что в этом конкретном примере он видит x(1)
и расширяет его, давая
y(1 x(2)))
Затем он идентифицирует вызов макроса y(1 x(2))
, с аргументом 1 x(2)
и предварительно сканирует, что для макросов расширяться.В рамках этого он находит x(2)
, который расширяется до y(2
, а затем вызывает ошибку из-за отсутствия )
для макроса y
.Обратите внимание, что в этот момент он все еще стремится расширить аргумент первого макроса y
, поэтому он смотрит на него изолированно БЕЗ рассмотрения остального входного файла, в отличие от расширения, которое имеет место для 6.10.3.4
Теперь возникает некоторый вопрос относительно того, должна ли это быть на самом деле ошибка, или же препроцессор должен обрабатывать эту последовательность y(2
как вообще не являющуюся макро-вызовом, так как ')' нет.Если он выполнит последнее, то увеличит этот y-вызов до 1 y(2
, который затем будет объединен с остальными входными данными ()
) и в конечном итоге расширится до 1 2