Зачем использовать MACRO + 0!= 0

В моей текущей кодовой базе я вижу следующий шаблон:

#if SOMETHING_SUPPORTED+0 != 0
...
#endif

К сожалению, это очень старая база кода, и никто не знает, как и почему она началась. Я думаю, что это началось в C, и оно было медленно преобразовано в C с классами, и теперь оно имеет тенденцию к С++

Я не вижу явного преимущества использования предыдущей конструкции вместо "классической", но, может быть, мне что-то не хватает:

#if SOMETHING_SUPPORTED
...
#endif

Знаете ли вы, зачем использовать #if MACRO+0 != 0 вместо #if MACRO?

Ответ 1

Ключевым моментом здесь является то, что база кода очень старая.

Этот трюк, вероятно, существует, потому что один раз код был перенесен в компилятор с очень старым препроцессором, который не обрабатывает макросы undefined как 0 в условных выражениях препроцессора #if.

То есть, с 1989 ANSI C было стандартизировано, что если мы имеем:

#if foo + bar - xyzzy

директива подлежит замене макроса, поэтому если foo, bar или xyzzy являются макросами, они заменяются. Затем любые оставшиеся идентификаторы, которые не были заменены, заменяются на 0. Поэтому, если foo определяется как 42, но bar и xyzzy вообще не определены, мы получаем:

#if 42 + 0 - 0

а не, скажем, плохой синтаксис:

#if 42 + -

или какое-либо другое поведение, например диагностика bar не определена.

В препроцессоре, где макросы undefined рассматриваются как пробелы, #if SOMETHING_SUPPORTED расширяется до всего лишь #if, что затем ошибочно.

Это единственный способ, с помощью которого трюк IDENT+0 делает какой-либо реальный смысл. Вы просто не захотите этого делать, если вы можете полагаться на предварительную обработку, соответствующую ISO C.

Причина в том, что если ожидается, что SOMETHING_SUPPORTED имеет числовые значения, это ошибочно рассеянно, чтобы определить его как просто пробел. В идеале вы хотите определить, когда это произошло, и прекратить компиляцию с помощью диагностики.

Во-вторых, если вы поддерживаете такое рассеянное использование, вы почти наверняка хотите, чтобы явно определенный, но пустой символ вел себя так, как будто он имел значение 1, а не значение 0. В противном случае вы создаете ловушку, Кто-то может сделать это в командной строке компилятора:

 -DSOMETHING_SUPPORTED=$SHELL_VAR  # oops, SHELL_VAR expanded to nothing

или в коде:

 #define SOMETHING_SUPPORTED  /* oops, forgot "1" */

Никто не собирается добавлять a #define или -D для символа с целью превратить выключить функцию, которой он управляет! Программист, который вставляет #define SOMETHING_SUPPORTED без 1, будет удивлен поведением

 #if SOMETHING_SUPPORTED+0

который пропускает материал, который должен был быть включен.

Вот почему я подозреваю, что мало кто из программистов C, читающих это, когда-либо видел такое использование, и почему я подозреваю, что это всего лишь обходной путь для поведения препроцессора, целью которого является пропустить блок, если отсутствует SOMETHING_SUPPORTED. Тот факт, что он закладывает "ловушку программиста", является лишь побочным эффектом обходного пути.

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

#ifndef SOMETHING_SUPPORTED
#define SOMETHING_SUPPORTED 0
#endif

а затем в другом месте используйте #if SOMETHING_SUPPORTED. Может быть, такой подход не возник для первоначального программиста, или, возможно, программист считал, что трюк +0 был опрятным и помещал значение в его сдерживание.

Ответ 2

#if X+0 != 0 отличается от #if X в случае, когда X определяется как пустое (обратите внимание: это отличается от случая, когда X не определяется), например:

#define X

#if X          // error
#if X+0 != 0   // no error; test fails

Очень часто задаются пустые макросы: конфигурация проекта может генерировать некоторый общий заголовок, содержащий пучок строк #define USE_FOO, #define USE_BAR, чтобы включить функции, поддерживаемые системой, и т.д.

!= 0 является избыточным, код может быть только #if X+0.


Таким образом, преимущество использования #if X+0 заключается в том, что если X определяется как пустая, то компиляция продолжается с пропуском блока, вместо запуска ошибки.

Хорошо ли это хорошая дискуссия, лично я использовал бы #ifdef для логических макросов, таких как USE_SOME_FEATURE и #if для макросов, где это значение может быть целым числом, например; и я хотел бы увидеть ошибку, если случайно использовать #if с чем-то определенным для пустого.

Ответ 3

Сделайте таблицу!

X       #if X     #if X+0 != 0
<undef> false     false
<empty> error     false
0       false     false
1       true      true
2       true      true
a       false     false
xyz     false     false
12a     error     error
12 a    error     error

Таким образом, единственное различие, которое мы нашли (спасибо комментаторам), - это случай, когда X определен, но не имеет значения (например, пустая строка). Я никогда раньше не видел вариант +0 != 0.

Ответ 4

Это еще один способ написания

 #if defined(MACRO) && MACRO != 0

+0 есть, чтобы гарантировать, что результатом является число. Если MACRO не определен, он избегает синтаксической ошибки, которая возникла бы из #if MACRO != 0.

Плохое качество, но не смущайтесь с ним, если не хотите.