Почему препроцессор C считает значения перечисления равными?
почему std::cout
строка в следующем коде выполняется, даже если A
и B
разные?
#include <iostream>
enum T { A = 1, B = 2 };
// #define A 1
// #define B 2
int main() {
#if (A == B)
std::cout << A << B;
#endif
}
если я использую #define
вместо этого (как прокомментировано), я не получаю никакого результата, как я ожидаю.
причина вопроса:
Я хочу, чтобы переключатель режимов на некоторые тестовый код, в котором я могу легко изменить режимы, комментируя/раскомментировав строки сверху:
enum T { MODE_RGB = 1, MODE_GREY = 2, MODE_CMYK = 3 };
// #define MODE MODE_RGB
#define MODE MODE_GREY
// #define MODE MODE_CMYK
int main() {
#if (MODE == MODE_RGB)
// do RGB stuff
#elif (MODE == MODE_GREY)
// do greyscale stuff
#else
// do CMYK stuff
#endif
// some common code
some_function(arg1, arg2,
#if (MODE == MODE_RGB)
// RGB calculation for arg3,
#elif (MODE == MODE_GREY)
// greyscale calculation for arg3,
#else
// CMYK calculation for arg3,
#endif
arg4, arg5);
}
Я знаю, что могу использовать числовые значения, например
#define MODE 1 // RGB
...
#if (MODE == 1) // RGB
но это делает код менее читаемым.
есть элегантное решение для этого?
7 ответов:
макросы не называются
A
илиB
С#if
линия,A
иB
заменить на0
, так что у вас на самом деле есть:enum T { A = 1, B = 2 }; int main() { #if (0 == 0) std::cout << A << B; #endif }
препроцессор запускается до того, как компилятор узнает что-либо о вашем
enum
. Препроцессор знает только о макросах (#define
).
это потому, что препроцессор работает до компиляции.
поскольку определения перечисления происходят во время компиляции, A и B будут определены как пустые (pp-number
0
) - и, таким образом, равно-во время предварительной обработки, и, таким образом, оператор вывода включается в скомпилированный код.при использовании
#define
они определяются по-разному во время предварительной обработки, и поэтому оператор оценивается как false.в отношении вашего комментария о том, что вы хотите сделать, вам не нужно использовать препроцессор
#if
для этого. Вы можете просто использовать стандартныйif
какMODE
иMODE_GREY
(илиMODE_RGB
илиMODE_CMYK
) все еще определено:#include <iostream> enum T { MODE_RGB = 1, MODE_GREY = 2, MODE_CMYK = 3 }; #define MODE MODE_GREY int main() { if( MODE == MODE_GREY ) std::cout << "Grey mode" << std::endl; else if( MODE == MODE_RGB ) std::cout << "RGB mode" << std::endl; else if( MODE == MODE_CMYK ) std::cout << "CMYK mode" << std::endl; return 0; }
другой вариант, используя только предварительный процессор, должен сделать это как @TripeHound правильно ответили ниже.
идентификаторы, которые не определены макросы интерпретируются как значение 0 в директивах условного препроцессора. Поэтому, поскольку вы не определили макрос
A
иB
оба они являются 0 и 0 равны друг другу.причина, по которой неопределенные (для препроцессора) идентификаторы считаются 0, заключается в том, что он позволяет использовать неопределенные макросы в условном без использования
#ifdef
.
препроцессор запускается перед компилятором, который означает, что препроцессор ничего не знает о символах определяются компилятором и поэтому он не может действовать в зависимости от них.
как и другие ответы, препроцессор C не видит перечислений. Он ожидает и может только понять макросы.
на стандарт C99, §6.10.1 (условное включение):
после выполнения всех замен, вызванных расширением макроса и определенным унарным оператором, все остальные идентификаторы заменяются на pp-число 0
другими словами, в директиве #if или #elif любые макросы, которые не могут быть расширенные, поскольку они не существуют / не определены, будут вести себя точно так же, как если бы они были определены как 0, и поэтому всегда будут равны друг другу.
вы можете поймать вероятное непреднамеренное поведение, подобное этому, в GCC/clang с опцией предупреждения -Wundef (вы, вероятно, захотите сделать его фатальным с-Werror=undef).
другие ответы объясняют, почему то, что вы пытаетесь не работает; для альтернативы, я бы, вероятно, пошел с:
#define RGB 1 #define GREY 2 #define CMYK 3 #define MODE RGB #if MODE == RGB //RGB-mode code #elif MODE == GREY //Greyscale code #elif MODE == CMYK //CMYK code #else # error Undefined MODE #endif
вам могут понадобиться префиксы на RGB/GREY / CMYK, если есть опасность столкновения с "реальным" исходным кодом.
сообщения объяснили, почему, но возможное решение для вас, которое сохраняет читаемость, может быть таким
#define MODE_RGB int main() { #ifdef MODE_RGB std::cout << "RGB mode" << std::endl; #elif defined MODE_GREY std::cout << "Grey mode" << std::endl; #elif defined MODE_CMYK std::cout << "CMYK mode" << std::endl; #endif }
вам просто нужно изменить макрос вверху, чтобы только макрос, который вас интересует, был определен. Вы также можете включить проверку, чтобы убедиться, что один и только один определен, а если нет, то и сделать
#error "You must define MODE_RGB, MODE_GREY or MODE_CMYK