Почему нет предупреждения с "#if X", когда X не определено? - PullRequest
20 голосов
/ 29 октября 2009

Я иногда пишу код примерно так:

// file1.cpp
#define DO_THIS 1

#if DO_THIS
    // stuff
#endif

Во время разработки кода я могу переключить определение DO_THIS между 0 и 1.

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

// file1.cpp
#define DO_THIS 1

и

// file2.cpp
#if DO_THIS
    // stuff
#endif

Очевидно, я исправил ошибку, но потом подумал, почему компилятор не предупредил меня? У меня установлен уровень предупреждения 4. Почему #if X не подозрительно, если X не определен?

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

РЕДАКТИРОВАТЬ: я могу понять, что без предупреждения с #ifdef это имеет смысл. Но, конечно же, #if отличается.

Ответы [ 8 ]

29 голосов
/ 29 октября 2009

gcc может выдать предупреждение за это, но это, вероятно, не требуется стандартом:

-Wundef
Предупреждать, если в директиве #if вычисляется неопределенный идентификатор.

17 голосов
/ 29 октября 2009

Опять же, как это часто бывает, ответ на вопрос «почему» справедлив: это было сделано именно так, потому что некоторое время назад было решено сделать это таким образом. Когда вы используете неопределенный макрос в #if, он заменяется на 0. Вы хотите знать, действительно ли он определен - используйте директиву defined().

Есть некоторые интересные преимущества этого подхода «по умолчанию в 0». Особенно, когда вы используете макросы, которые могут быть , определяемыми платформой, а не вашими собственными макросами.

Например, некоторые платформы предлагают макросы __BYTE_ORDER, __LITTLE_ENDIAN и __BIG_ENDIAN для определения их порядка байтов. Вы можете написать директиву препроцессора, например

#if __BYTE_ORDER == __LITTLE_ENDIAN
  /* whatever */
#else
  /* whatever */
#endif

Но если вы попытаетесь скомпилировать этот код на платформе, которая вообще не определяет эти нестандартные макросы (т.е. ничего о них не знает), приведенный выше код будет преобразован препроцессором в

#if 0 == 0
...

и версия кода с прямым порядком байтов будет скомпилирована «по умолчанию». Если вы написали оригинал #if как

#if __BYTE_ORDER == __BIG_ENDIAN
...

тогда версия кода с прямым порядком байтов будет скомпилирована "по умолчанию".

Я не могу сказать, что #if было определено так, как это было специально для уловок, подобных приведенным выше, но иногда бывает полезно.

1 голос
/ 24 июля 2018

Если DO_THIS - это ваше определение, тогда простое и работающее решение - использование функционального макроса:

#define DO_THIS() 1

#if DO_THIS()
    //stuff
#endif

Я проверял это в Visual Studio 2008, 2015 и GCC v7.1.1. Если DO_THIS () не определено, VS генерирует:

предупреждение C4067: неожиданные токены после директивы препроцессора - ожидается перевод строки

и GCC генерирует

ошибка: отсутствует двоичный оператор перед токеном "("

1 голос
/ 22 февраля 2017

Есть рекурсивная проблема. Если у вас есть

#define MODEL  MODEL_A

#if (MODEL == MODEL_B)
 // Surprise, this is compiled!
#endif

если определения MODEL_A и MODEL_B отсутствуют, то оно будет скомпилировано.

#ifdef MODEL
#error Sorry, MODEL Not Defined
// Surprise, this error is never reached (MODEL was defined by undefined symbol!)
#endif 

#ifdef MODEL_B
#error Sorry, MODEL_B Not Defined
// This error is reached
#endif 
1 голос
/ 15 марта 2015

Если вы отчаянно пытаетесь предотвратить подобные ошибки, попробуйте следующее, использующее магию вставки токенов препроцессора и оценку выражений для обеспечения определения макроса (и 0 в этом примере):

#define DEFINED_VALUE(x,y) (defined (y##x) ? x : 1/x)
#if DEFINED_VALUE(FEATURE1,) == 0
1 голос
/ 17 мая 2013

Когда вы не можете использовать компилятор с предупреждающим сообщением (например, -Wundef в gcc), я нашел один несколько полезный способ генерировать ошибки компилятора.

Конечно, вы всегда можете написать:

#ifndef DO_THIS
    error
#endif
#if DO_THIS

Но это действительно раздражает

Немного менее раздражающий метод:

#if (1/defined(DO_THIS) && DO_THIS)

Это сгенерирует ошибку деления на ноль, если DO_THIS не определено. Этот метод не идеален, потому что идентификатор указан дважды, а неправильное написание во втором вернет нас к тому, с чего мы начали. Это выглядит странно тоже. Похоже, должен быть более чистый способ сделать это, например:

#define PREDEFINED(x) ((1/defined(x)) * x)
#if PREDEFINED(DO_THIS)

но на самом деле это не работает.

0 голосов
/ 29 октября 2009

Если я правильно об этом думаю.

Директивы препроцессора обрабатываются до компиляции любого исходного кода. На этом этапе (этапах) перевода, в котором это происходит, обрабатываются все директивы препроцессора, макросы и т. Д., А затем компилируется фактический исходный код.

Поскольку #if используется, чтобы определить, был ли определен X, и выполнить какое-либо действие, если оно было или не было определено. #If во фрагменте кода будет компилироваться без каких-либо ошибок, потому что в отношении компилятора ошибок нет. Вы всегда можете создать заголовочный файл с конкретными #defines, которые понадобятся вашему приложению, а затем включить этот заголовок.

0 голосов
/ 29 октября 2009

Компилятор не выдал предупреждение, потому что это директива препроцессора. Он оценивается и разрешается до того, как компилятор его видит.

...