Разница между 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}