#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
。