Стандартное поведение препроцессора Visual C ++

Я изучаю стандарт C ++ на точное поведение препроцессора (мне нужно реализовать какой-то препроцессор C ++). Из того, что я понимаю, приведенный ниже пример (чтобы помочь моему пониманию) должен быть верным:

#define dds(x) f(x,
#define f(a,b) a+b
dds(eoe)
su)

Я ожидаю, что первая функция, как макрос вызова dds(eoe) заменить на f(eoe, (обратите внимание на запятую в строке замены), которая затем считается f(eoe,su) когда вход повторно сканируется.

Но тест с VC ++ 2010 дал мне это (я сказал VC ++ выводить предварительно обработанный файл):

eoe+et_leoe+et_l
su)

Это нелогично и, очевидно, неверно. Это ошибка в VC ++ 2010 или мое неправильное понимание стандарта C ++? В частности, неправильно ли ставить запятую в конце строки замены, как я? Мое понимание стандартной грамматики C ++ заключается в том, что любой preprocessing-tokenтам разрешено

РЕДАКТИРОВАТЬ:

У меня нет GCC или других версий VC ++. Может ли кто-нибудь помочь мне проверить с этими компиляторами.

15

Решение

Насколько я понимаю, в [cpp.subst/rescan] части стандарта, которые делают то, что вы делаете незаконным, и лязг а также НКУ правы в расширении его как eoe+suи поведение MSC (Visual C ++) должно быть сообщено как ошибка.

Я не смог заставить его работать, но мне удалось найти уродливый обходной путь MSC для вас, используя variadics — вы можете найти это полезным, или нет, но в любом случае это:

#define f(a,b) (a+b
#define dds(...) f(__VA_ARGS__)

Расширяется как:

(eoe+
su)

Конечно, это не будет работать с НКУ а также лязг.

2

Другие решения

Мой ответ действителен для препроцессора C, но в соответствии с Является ли препроцессор C ++ идентичным препроцессору C?, различия не имеют отношения к этому делу.

От C, Справочное руководство, 5-е издание:

Когда вызывается функциональный вызов макроса, весь вызов макроса
после обработки параметров заменяется копией тела. параметр
Обработка происходит следующим образом. Фактические аргументы
связан с соответствующими именами формальных параметров. Копия
затем создается тело, в котором каждое вхождение формального параметра
имя заменяется копией фактической последовательности токена параметра
связано с этим. Эта копия тела затем заменяет макрос
вызов.
[…] После того, как макро вызов был расширен, проверка на макро вызовы
возобновляется в начале расширения, чтобы имена макросов могли
быть признанным в рамках расширения с целью дальнейшего макроса
замена.

Обратите внимание на слова в рамках расширения. Вот что делает ваш пример недействительным. Теперь, объедините это с этим: ОБНОВИТЬ: читайте комментарии ниже.

[…] Макрос вызывается написанием его имени, левой круглой скобки,
затем один раз фактическая последовательность токенов аргумента для каждого формального параметра,
тогда правильная скобка. Фактические последовательности токенов аргумента
разделенных запятыми.

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

Это может быть трудно думать, но я считаю, что то, что должен случиться с вашим примером является то, что имя макроса f распознается во время повторного сканирования, и поскольку последующая обработка токена выявляет макро-вызов для f()Ваш пример верен и должен выводить то, что вы ожидаете. GCC и clang дают правильный вывод, и в соответствии с этим рассуждением это также будет допустимо (и даст эквивалентные результаты):

#define dds f
#define f(a,b) a+b

dds(eoe,su)

И действительно, результаты предварительной обработки одинаковы в обоих примерах. Что касается вывода, который вы получаете с VC ++, я бы сказал, что вы нашли ошибку.

Это соответствует разделу 6.10.3.4 C99, а также разделу 16.3.4 стандарта C ++, Повторное сканирование и дальнейшая замена:

После того, как все параметры в списке замены были заменены и # и ##
обработка выполнена, все маркеры предварительной обработки меток удалены. Затем
результирующая последовательность токена предварительной обработки пересматривается вместе со всеми последующими
предварительная обработка токенов исходного файла для замены большего количества имен макросов.

8

Ну, проблема, которую я вижу в том, что препроцессор делает следующее

ddx (x) становится f (x,

Однако, f (x, также определено (даже если оно определено как f (a, b)), поэтому f (x, расширяется до x + мусор).

Таким образом, ddx (x) в конечном итоге превращается в x + мусор (потому что вы определили f (smthing,).

Ваш dds (eoe) фактически расширяется в a + b, где a — eoe, а b — et_l.
И делает это дважды по любой причине :).

Этот сценарий, который вы создали, зависит от компилятора и зависит от того, как препроцессор выберет обработку определения расширения.

1
По вопросам рекламы [email protected]