#ifdef和#if – 这是更好/更安全的方法来启用/禁用编译特定部分的代码?

这可能是一个风格问题,但我们的开发团队有一点分歧,我想知道是否有人对此有任何想法…

基本上,我们有一些debugging打印语句,我们在正常的开发过程中closures。 我个人更喜欢做以下事情:

//---- SomeSourceFile.cpp ---- #define DEBUG_ENABLED (0) ... SomeFunction() { int someVariable = 5; #if(DEBUG_ENABLED) printf("Debugging: someVariable == %d", someVariable); #endif } 

虽然有些团队喜欢以下内容:

 // #define DEBUG_ENABLED ... SomeFunction() { int someVariable = 5; #ifdef DEBUG_ENABLED printf("Debugging: someVariable == %d", someVariable); #endif } 

…这些方法哪个更适合你,为什么? 我的感觉是,第一个是更安全的,因为总是有一些定义,没有任何危险可以破坏其他定义。

当然 ,我最初的反应是#ifdef但我认为#if实际上有一些显着的优势 – 这是为什么:

首先,您可以在预处理器编译的testing中使用DEBUG_ENABLED 。 示例 – 通常情况下,当启用debugging时,我想要更长的超时,所以使用#if ,我可以写这个

  DoSomethingSlowWithTimeout(DEBUG_ENABLED? 5000 : 1000); 

… 代替 …

 #ifdef DEBUG_MODE DoSomethingSlowWithTimeout(5000); #else DoSomethingSlowWithTimeout(1000); #endif 

其次,如果你想从#define迁移到全局常量,那么你处于一个更好的位置。 #define通常被大多数C ++程序员所忽视。

第三,你说你在团队中有分歧。 我的猜测是这意味着不同的成员已经采取了不同的方法,并且需要标准化。 判定#if是首选选项,意味着即使在DEBUG_ENABLED为false时,使用#ifdef代码也会编译并运行。 而追踪和删除产生的debugging输出要简单得多,反之亦然。

哦,还有一个小的可读性点。 你应该可以在你的#define使用true / false而不是0/1,并且因为这个值是一个单一的词法标记,所以你一次不需要括号。

 #define DEBUG_ENABLED true 

代替

 #define DEBUG_ENABLED (1) 

他们都是可怕的。 相反,这样做:

 #ifdef DEBUG #define D(x) do { x } while(0) #else #define D(x) do { } while(0) #endif 

然后每当你需要debugging代码时,把它放在D(); 。 而你的程序没有被#ifdef可怕迷宫污染。

#ifdef只是检查是否定义了一个标记,给定

 #define FOO 0 

然后

 #ifdef FOO // is true #if FOO // is false, because it evaluates to "#if 0" 

我们在多个文件中遇到了同样的问题,并且总是有人忘记包含“特征标记”文件(使用> 41,000个文件的代码库很容易)的问题。

如果你有feature.h:

 #ifndef FEATURE_H #define FEATURE_H // turn on cool new feature #define COOL_FEATURE 1 #endif // FEATURE_H 

但是,然后你忘记在file.cpp中包含头文件:

 #if COOL_FEATURE // definitely awesome stuff here... #endif 

那么你有一个问题,在这种情况下,编译器将COOL_FEATURE解释为“false”,并且不包含代码。 是的gcc确实支持一个标志,导致未定义macros的错误…但大多数第三方代码定义或不定义的function,所以这不会是便携式。

我们采用了一种便携的方式来纠正这种情况,以及testing一个function的状态:functionmacros。

如果您将上面的feature.h更改为:

 #ifndef FEATURE_H #define FEATURE_H // turn on cool new feature #define COOL_FEATURE() 1 #endif // FEATURE_H 

但是,你又忘了将头文件包含在file.cpp中:

 #if COOL_FEATURE() // definitely awseome stuff here... #endif 

预处理器会由于使用未定义的函数macros而出错。

我认为这完全是一个风格问题。 两者都没有明显的优势。

一致性比任何一个select都更重要,所以我build议你和你的团队聚在一起,select一种风格,坚持下去。

为了执行条件编译,#if和#ifdef 几乎相同,但不完全相同。 如果你的条件编译依赖于两个符号,那么#ifdef将不能工作。 例如,假设你有两个条件编译符号,PRO_VERSION和TRIAL_VERSION,你可能有这样的东西:

 #if defined(PRO_VERSION) && !defined(TRIAL_VERSION) ... #else ... #endif 

上面使用#ifdef变得复杂得多,特别是让#else部分工作。

我使用广泛使用条件编译的代码,我们有#if&#ifdef的混合。 我们倾向于使用#ifdef /#ifndef作为简单的例子,并且每当有两个或更多的符号被评估的时候。

我自己更喜欢:

 #if defined(DEBUG_ENABLED) 

因为它更容易创build看起来相反的条件的代码更容易find:

 #if !defined(DEBUG_ENABLED) 

 #ifndef(DEBUG_ENABLED) 

这是一个风格问题。 但是我推荐一个更简洁的方法来做到这一点:

 #ifdef USE_DEBUG #define debug_print printf #else #define debug_print #endif debug_print("i=%d\n", i); 

你这样做一次,然后总是使用debug_print()来打印或什么也不做。 (是的,在这两种情况下都会编译。)这样,你的代码就不会被预处理指令混淆了。

如果你得到“expression无效”的警告,并想摆脱它,这里有一个替代scheme:

 void dummy(const char*, ...) {} #ifdef USE_DEBUG #define debug_print printf #else #define debug_print dummy #endif debug_print("i=%d\n", i); 

#if给你select将其设置为0来closuresfunction,同时仍然检测到开关在那里。
我个人总是#define DEBUG 1所以我可以用#if或#ifdef来捕捉它

#if和#define MY_MACRO(0)

使用#if意味着你创build了一个“定义”macros,也就是在代码中被search的东西,用“(0)”代替。 这是我讨厌在C ++中看到的“macros观地狱”,因为它污染了具有潜在代码修改的代码。

例如:

 #define MY_MACRO (0) int doSomething(int p_iValue) { return p_iValue + 1 ; } int main(int argc, char **argv) { int MY_MACRO = 25 ; doSomething(MY_MACRO) ; return 0; } 

在g ++上给出以下错误:

 main.cpp|408|error: lvalue required as left operand of assignment| ||=== Build finished: 1 errors, 0 warnings ===| 

只有一个错误。

这意味着你的macros成功地与你的C ++代码交互:对函数的调用是成功的。 在这种简单的情况下,这是有趣的。 但是,我自己的代码与macros默默玩耍的经验不充满欢乐和fullfilment,所以…

#ifdef和#define MY_MACRO

使用#ifdef意味着你“定义”了一些东西。 不是你给它一个价值。 它仍然是污染的,但至less,它会被“没有任何东西取代”,而不被C ++代码看作是延迟代码语句。 上面相同的代码,用一个简单的定义,它:

 #define MY_MACRO int doSomething(int p_iValue) { return p_iValue + 1 ; } int main(int argc, char **argv) { int MY_MACRO = 25 ; doSomething(MY_MACRO) ; return 0; } 

给出以下警告:

 main.cpp||In function 'int main(int, char**)':| main.cpp|406|error: expected unqualified-id before '=' token| main.cpp|399|error: too few arguments to function 'int doSomething(int)'| main.cpp|407|error: at this point in file| ||=== Build finished: 3 errors, 0 warnings ===| 

所以…

结论

我宁愿在我的代码中没有macros,但由于多种原因(定义头文件或debuggingmacros),我不能。

但至less,我喜欢使他们与我的合法C ++代码交互的可能性最小。 这意味着使用#define没有价值,使用#ifdef和#ifndef(或者甚至#if如Jim Buck所build议的那样),最重要的是,给他们这么长的名字,以及他/她的正确头脑中没有人会使用它是“偶然的”,而且决不会影响合法的C ++代码。

发布Scriptum

现在,当我重新阅读我的文章时,我不知道是否应该find一些永远不会正确的C ++添加到我的定义中的值。 就像是

 #define MY_MACRO @@@@@@@@@@@@@@@@@@ 

这可以用#ifdef和#ifndef,但不让代码编译,如果在一个函数内使用…我试过这个成功的g ++,它给了错误:

 main.cpp|410|error: stray '@' in program| 

有趣。 🙂

第一个似乎更清楚。 与定义/未定义相比,它看起来更自然。

两者完全相同。 在习惯用法中,#ifdef只是用来检查定义(以及我在你的例子中使用的),而#if用在更复杂的expression式中,比如#if defined(A)&&!defined(B)。

有点OT,但在预处理器中打开/closureslogging在C ++中肯定是次优的。 有很好的日志logging工具,比如Apache的log4cxx ,它们是开源的,不限制你分发应用程序的方式。 它们还允许您在不重新编译的情况下更改日志logging级别,如果您closures了日志logging,则开销非常低,并使您有机会在生产中完全closures注销。

这根本不是一个风格问题。 此外,问题是不幸的是错误的。 您无法比较这些预处理器指令的更好或更安全的意义。

 #ifdef macro 

的意思是“如果macros定义”或“如果macros存在”。 macros的价值在这里并不重要。 它可以是什么。

 #if macro 

如果总是比较一个值。 在上面的例子中是标准的隐式比较:

 #if macro !=0 

使用#if的例子

 #if CFLAG_EDITION == 0 return EDITION_FREE; #elif CFLAG_EDITION == 1 return EDITION_BASIC; #else return EDITION_PRO; #endif 

你现在可以把CFLAG_EDITION的定义放在你的代码中

 #define CFLAG_EDITION 1 

或者您可以将macros设置为编译器标志。 另见这里 。

我一直使用#ifdef和编译器标志来定义它…

或者,你可以声明一个全局常量,如果使用C ++而不是预处理器#if。 编译器应该优化未使用的分支给你,并且你的代码将会更干净。

以下是Stephen C. Dewhurst所说的关于使用#if的C ++技巧 。

我曾经使用#ifdef ,但是当我转换到Doxygen的文档,我发现注释掉的macros不能被logging(或至less,Doxygen产生警告)。 这意味着我不能logging当前未启用的function切换macros。

虽然可以仅为Doxygen定义macros,这意味着代码的非活动部分中的macros也将被logging。 我个人想要显示function开关,否则只logging当前select的内容。 而且,如果只有在Doxygen处理文件时才定义许多macros,那么代码就会变得非常混乱。

因此,在这种情况下,最好总是定义macros并使用#if