Несоответствия препроцессора в Visual Studio - PullRequest
0 голосов
/ 27 сентября 2018

Я компилирую следующий код C ++ в Visual Studio 2015 (обновление 3):

#include <iostream>
using namespace std;

////////////////////////////////////////

#define UNDERSCORE1(a,b) a ## _ ## b
#define UNDERSCORE(a,b) UNDERSCORE1(a,b)

#define STRINGIFY1(x) #x
#define STRINGIFY(x) STRINGIFY1(x)

#define VALUE(x) UNDERSCORE(x, VALUE)
#define NEXT(x) (VALUE(x) + 1)

/////////////////////////////////////////

#define X1_VALUE 0
#define X2_VALUE NEXT(X1)
#define X3_VALUE NEXT(X2)
#define TOTAL NEXT(X3)

int main() {
    cout << STRINGIFY(TOTAL) << endl;
    cout << TOTAL << endl;
    return 0;
}

Результат, выводимый на стандартный вывод, очень странный:

(X3_VALUE + 1)
3

При попыткето же самое для gcc, сборка завершается неудачно (как и ожидалось).
При комментировании cout << TOTAL << endl; я получаю что-то совсем другое в целом:

(NEXT(X2) + 1)

На самом деле поведение gcc имеет смысл, посколькуМакрос NEXT вызывается рекурсивно: NEXT(X3) расширяется до X3_VALUE, который, в свою очередь, расширяется до NEXT(X2), поэтому второе расширение макроса NEXT (NEXT(X2)) не выполняется.

Что не имеет смысла, так это поведение Visual Studio:

  • При печати макроса TOTAL с использованием STRINGIFY, NEXT кажется расширенным в два раза для получения X3_VALUE.
  • При компиляции макроса TOTAL напрямую для отправки его в cout, NEXT полностью расширяется!Как будто препроцессор запускался несколько раз, чтобы рекурсивно развернуть NEXT.

Еще одна вещь, которую я пытался скомпилировать в Visual Studio с опцией компилятора /P, чтобы получить предварительно обработанный код:

int main() {
    cout << "(X3_VALUE + 1)" << endl;
    cout << (((0 + 1) + 1) + 1) << endl;
    return 0;
}

  • Так это, как я подозреваю, ошибка в препроцессоре Visual Studio?Или законное неопределенное поведение?
  • Возможно, этим поведением можно злоупотребить, чтобы действительно рекурсивно расширить макросы?Я знаю, что ограниченная рекурсия возможна с некоторыми уловками , но ограничена заранее определенным числом сканирований.В этом случае я не наблюдал ограничения на количество раз, NEXT расширяется.

1 Ответ

0 голосов
/ 23 ноября 2018

Как отметил Ганс в комментариях, препроцессор MSVC не соответствует.

Вы можете включить соответствующий препроцессор с помощью -experimental:preprocessor.

Вот упрощенное решение repro +: https://godbolt.org/z/7u_-bH

...