Разница между gcc и препроцессором Microsoft
Я обнаружил, что компилятор Microsoft Visual Studio и gcc предварительно обрабатывают следующий небольшой фрагмент по-разному:
# define M3(x, y, z) x + y + z
# define M2(x, y) M3(x, y)
# define P(x, y) {x, y}
# define M(x, y) M2(x, P(x, y))
M(a, b)
'gcc -E' дает следующее:
a + {a + b}
а 'cl/E' выдает предупреждение о отсутствующем аргументе макроса и выводит следующий результат:
a + {a, b} +
Кажется, что запятые, которые поступают из вложенных расширений макросов, не считаются разделителями аргументов. К сожалению, я не нашел описания алгоритма, реализованного в препроцессоре cl, и поэтому я не уверен, что мое предложение верное. Кто-нибудь знает, как работает препроцессор cl и какая разница между его алгоритмом и gcc? И как можно объяснить наблюдаемое поведение?
Ответы
Ответ 1
# define M3(x, y, z) x + y + z
# define M2(x, y) M3(x, y)
# define P(x, y) {x, y}
# define M(x, y) M2(x, P(x, y))
M(a, b)
Давайте разворачиваем это вручную, шаг за шагом:
M(a, b)
--> M2(a, P(a, b))
--> M2(a, {a, b})
В стандарте говорится:
Отдельные аргументы в списке разделяются запятой токены предварительной обработки, но токеты предварительной обработки запятой между совпадающими внутренними скобками не разделяют
указаны только скобки, поэтому...
--> M3(a, {a, b})
--> a + {a + b}
Важно:
M3(a, {a, b})
Здесь, согласно предыдущей цитате из стандарта, три "аргумента" передаются в M3 (используя одинарные кавычки для описания токенов/аргументов):
M3('a', '{a', 'b}')
которые расширяются до
'a' + '{a' + 'b}'
И это то, что cpp
(4.6.1) дает дословно:
# 1 "cpp.cpp"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "cpp.cpp"
a + {a + b}
cpp
(или gcc
и g++
) верны, MSVC - нет.
Как дворянин, убедитесь, что существует отчет об ошибке.
Ответ 2
Единственная логика, объясняющая такое поведение, выглядит следующим образом.
Способ CL:
M(a,b)
M2(a,P(a,b))
M3(a,P(a,b))
M3(a,{a,b}) -> M3 gets 2 arguments ( 'a' and '{a,b}') instead of 3.
| \ /
arg1 |
arg2
Способ Gcc:
M(a,b)
M2(a,P(a,b))
M3(a,P(a,b))
M3(a,{a,b}) -> Gcc probably thinks there are 3 arguments here ('a', '{a', 'b}').
| | |
arg1 | |
arg2 |
arg3
Ответ 3
Я думаю, что gcc правильно понимает, что делает Microsoft неправильно.
Когда выполняется макроподстановка для строки
M2(a, P(a, b))
стандарт (раздел 6.10.3.1) требует, чтобы перед заменой второго параметра ( "y" ) в списке замены макроса ( "M3 (x, y)" ) с его аргументом ( "P (a, b)" ), для этого аргумента должна быть выполнена замена макроса. Это означает, что "P (a, b)" обрабатывается до "{a, b}" перед его вставкой, в результате чего
M3(a, {a, b})
который затем заменяется на
a + {a + b}