Какое значение ожидать от макроса, который был установлен в результате работы методаdefined()?

Какова ожидаемая ошибка компилятора в приведенной ниже очень простой программе на C? gcc дает мне 1, тогда как MSVC 2013 дает мне 2.

#define foo
#define bar (defined(foo))

#if bar
#error 1
#else
#error 2
#endif

Надеюсь, мои вопросы одинаково просты:

  1. Что говорит спецификация C о значении методаdefined()? Кажется, я не могу найти ничего, что говорило бы об установке его значения для другого макроса.
  2. Фактический код не является чем-то, что я могу контролировать, и «#if bar» используется повсюду. Каков самый простой способ изменить #define, чтобы панель #if работала в MSVC "как ожидалось"? Единственное, о чем я могу думать, это расширить его:

.

#ifdef foo
#define bar 1
#else
#define bar 2
#endif

person o.c.    schedule 03.12.2014    source источник
comment
Я проверил это на себе, это странно. Я не могу представить, что MSVC думает, что он здесь делает.   -  person Mooing Duck    schedule 03.12.2014


Ответы (2)


Спецификация C говорит:

§6.10.1/1 Выражение ... может содержать выражения унарных операторов формы defined identifier или defined(identifier), которые оцениваются как 1, если идентификатор в настоящее время определен как имя макроса (то есть, если он предопределен или если он был предмет директивы предварительной обработки #define без промежуточной директивы #undef с тем же идентификатором субъекта), 0, если это не так.

Вызовы макросов §6.10.1/4 в списке токенов предварительной обработки, которые станут управляющим постоянным выражением, заменяются (за исключением имен макросов, измененных унарным оператором defined), как и в обычном тексте. Если токен defined генерируется в результате этого процесса замены или использование унарного оператора defined не соответствует одной из двух указанных форм до замены макроса, поведение не определено сильный>. После выполнения всех замен из-за раскрытия макроса и унарного оператора defined все оставшиеся идентификаторы (в том числе лексически идентичные ключевым словам) заменяются на pp-номер 0, а затем каждый токен препроцессинга конвертируется в токен.

(выделено мной) Однако замена макросов очень сложна, и я думаю, что MSVC определяет foo как defined(bar), что является неопределенным поведением, тогда как GCC правильно определяет foo как 1. Поскольку MSVC в таком случае находится в неопределенном поведении, он делает странные вещи.

Как вы говорите, самое простое решение

#ifdef foo
#define bar 1
#else
#define bar 2
#endif
person Mooing Duck    schedule 03.12.2014
comment
Я не думаю, что что-то не так с тем, что делает MSVC. В этом случае токен defined генерируется в результате … процесса замены. В спецификации четко сказано, что это UB, поэтому поставщик реализации может выбирать любой вариант действий. Компилятор мог бы даже включить обе ветви условного фрагмента, это было бы легальным источником удовольствия. - person ach; 03.12.2014
comment
@AndreyChernyakhovskiy: Я тоже сделал вывод UB, но при перечитывании мои рассуждения были неверны; ты прав. - person Mooing Duck; 03.12.2014
comment
Я перестал читать спецификацию на один абзац раньше срока. Я понял, что это UB, но хотел убедиться. Спасибо за помощь! - person o.c.; 04.12.2014

Я полагаю, что компилятор увидит все после того, как имя/макрос будет определено как часть определения имени/макроса, то есть как текст программы, а не как текст макроса.

int defined(char s);    // prototype of a function named "defined"

#define foo
#define bar defined(foo)

«bar» в любом месте текста программы теперь будет заменен вызовом define() без аргументов (поскольку «foo» определено как пустое).

person Paul Ogilvie    schedule 03.12.2014
comment
defined — это волшебная функция препроцессора › Любая функция C/C++ с таким же именем, вероятно, будет проигнорирована. (Наличие такой вещи может быть неопределенным поведением, мне нужно проверить) - person Mooing Duck; 03.12.2014
comment
@MooingDuck, почему ты так думаешь? defined имеет особое значение только в условном выражении #if и #elif. В противном случае вполне законно использовать defined в качестве идентификатора. - person ach; 03.12.2014
comment
Я подтвердил, что defined не является ключевым словом, так что да, оно определено только как имеющее особое значение в if и elif, в которых контексты, любая функция C/C++ с тем же именем предположительно игнорируется. (Мне очень интересно, что делают компиляторы, если у них есть #define defined(x) 1) - person Mooing Duck; 03.12.2014
comment
И почему «определенный» как текст программы игнорируется? Если это не ключевое слово C/C++, если оно не удалено препроцессором (поскольку оно не используется в #if/elif), оно остается как текст программы и будет скомпилировано. - person Paul Ogilvie; 04.12.2014