Действительно ли #defining внутри блока - это запах кода? - PullRequest
0 голосов
/ 17 мая 2018

Я читал, что #defining и #undefining внутри блока - это запах кода. Это почему? Кроме того, я вижу, что в коде (просто пример, который показывает, что у меня есть в реальном коде), это компилируется,

if(x == 1000) {
    #define MACRO_EXAMPLE 1
} else {
    #define MACRO_EXAMPLE 1
}

Но это не компилируется,

if(x == 1000) {
    #define MACRO_EXAMPLE 1
} else {
    #define MACRO_EXAMPLE 2
}

с ошибкой warning C4005: 'MACRO_EXAMPLE' : macro redefinition

Как это обрабатывается? Как это работает?

Ответы [ 5 ]

0 голосов
/ 17 мая 2018

Директивы препроцессора не обращают никакого внимания на блокировку области видимости, и они оказывают свое влияние во время компиляции, несмотря ни на что. Пример вашего кода

if(x == 1000) {
    #define MACRO_EXAMPLE 1
} else {
    #define MACRO_EXAMPLE 2
}

на 100% эквивалентно написанию

    #define MACRO_EXAMPLE 1
    #define MACRO_EXAMPLE 2
if(x == 1000) {
} else {
}

(за исключением, возможно, номеров строк в отладочной информации). Теперь должно быть более очевидно, почему это ошибка. Именно поэтому определение внутри блока является запахом кода - они имеют независимо от области действия файла.

(Я смутно помню предложение изменить это только в C ++, но я не думаю, что оно никуда не ушло.)

Это - это хороший стиль для определения макроса, который только одна конструкция верхнего уровня может использовать в какой-либо бизнес-структуре непосредственно перед этой конструкцией, и повторное определение его сразу же после этого. Вы увидите это с X-macros , например:

#define X(a, b, c) b,
const int b_tbl[] = {
    #include "tbl.inc"
};
#undef X

Но обратите внимание, что #define и #undef находятся вне определения b_tbl, поэтому они по-прежнему считываются человеку как область видимости файла.

0 голосов
/ 17 мая 2018

Как это обрабатывается?Как это работает?

Строки if-else не влияют на препроцессор.

Что касается препроцессора, эти строки могут и не существовать.

Процессор заботится только о

    #define MACRO_EXAMPLE 1
    #define MACRO_EXAMPLE 1

в первом случае и

    #define MACRO_EXAMPLE 1
    #define MACRO_EXAMPLE 2

во втором случае.

0 голосов
/ 17 мая 2018

Макросы обрабатываются препроцессором, который выполняется до фактической компиляции.Препроцессор рассматривает файлы как обычный текст с логикой препроцессора, отличной от логики исходного кода.используйте undef

#undef MACRO_EXAMPLE

Это отменит его, так что вы можете переопределить его позже.Это плохо разработано, но будет работать .

if(x == 1000) {
    #define MACRO_EXAMPLE 1
    //some code using the macro. Here it is expanded as 1
    #undef MACRO_EXAMPLE
} else {
    #define MACRO_EXAMPLE 2
    //some code using the macro. Here it is expanded as 2
    #undef MACRO_EXAMPLE
}
0 голосов
/ 17 мая 2018

предварительная обработка происходит перед анализом вашего кода. Это означает, что if / else игнорируется и

if(x == 1000) {
    #define MACRO_EXAMPLE 1
} else {
    #define MACRO_EXAMPLE 1
}

фактически рассматривается как

#define MACRO_EXAMPLE 1
#define MACRO_EXAMPLE 1

что нормально, потому что вы используете одно и то же значение. С другой стороны

if(x == 1000) {
    #define MACRO_EXAMPLE 1
} else {
    #define MACRO_EXAMPLE 2
}

становится

#define MACRO_EXAMPLE 1
#define MACRO_EXAMPLE 2

и так как значение макроса изменилось, это ошибка

0 голосов
/ 17 мая 2018

Препроцессор, который обрабатывает такие вещи, как #define, работает концептуально до анализа остальной части вашей программы. Среди прочего, это означает, что определения препроцессора не учитывают область видимости блока. Компилятор не замечает, если вы сделали #define внутри блока { ... }. Не существует механизма автоматического выполнения #undef в конце блока { ... }.

Вы можете сделать это вручную, если хотите: вы можете использовать #define внутри блока и не забывать делать свои собственные, явные #undef в конце блока. Но это все на вас: компилятор не проверит вашу работу или не предупредит вас, если вы ошиблись.

И по этой причине это считается плохой практикой. (Я думаю, это то, что вы подразумеваете под «запахом кода».) Это плохая практика, потому что она подвержена ошибкам, и нет хорошего автоматического способа отловить любые ошибки.

Если вы вообще используете препроцессор #define, он должен быть глобальным (потому что, по сути, является глобальным, хотите вы того или нет).

И, конечно же, глобальные переменные тоже не одобряются. И это связано с тем, что в наши дни препроцессор #define в значительной степени недоволен, все они.

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

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

...