这可能是一种风格问题,但我们的开发团队有一些分歧,我想知道是否有其他人对此事有任何想法......

基本上,我们有一些调试打印语句,我们在正常开发期间关闭。我个人更喜欢做以下事情:

//---- 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 实际上有一些明显的优势 - 这就是为什么:

首先,您可以在预处理器编译的测试中使用 DEBUG_ENABLED 。示例 - 通常,在启用调试时我想要更长的超时,所以使用 #if ,我可以写这个

  DoSomethingSlowWithTimeout(DEBUG_ENABLED? 5000 : 1000);

......而不是......

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

其次,如果您想从 #define 迁移到全局常量,那么您处于更好的位置。大多数C ++程序员通常不赞成 #define

而且,第三,你说你的团队存在分歧。我的猜测是,这意味着不同的成员已采用不同的方法,您需要标准化。判断 #if 是首选,这意味着使用 #ifdef 的代码将编译 - 并运行 - 即使 DEBUG_ENABLED 为false。并且很多更容易跟踪并删除在不应该反之亦然时产生的调试输出。

哦,还有一个小的可读性点。您应该能够在 #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

然后,无论何时需要调试代码,请将其放在 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

但是你忘了在header.cpp中包含头文件:

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

然后你遇到问题,编译器将COOL_FEATURE解释为未定义为“false”。在这种情况下,无法包含代码。是的gcc确实支持一个导致未定义宏错误的标志......但大多数第三方代码定义或不定义功能,所以这不是那么便携。

我们采用了一种便携式方法来纠正这种情况,并测试功能的状态:功能宏。

如果您将上述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

由于使用了未定义的函数宏,预处理器会出错。

为了执行条件编译,#if和#ifdef 几乎相同,但不完全相同。如果条件编译依赖于两个符号,则#ifdef也不会起作用。例如,假设您有两个条件编译符号PRO_VERSION和TRIAL_VERSION,您可能会这样:

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

使用#ifdef变得更加复杂,特别是让#else部分工作。

我在广泛使用条件编译的代码上工作,我们混合了#if& #ifdef来。对于简单情况,我们倾向于使用#ifdef /#ifndef;对于正在评估的两个或更多符号,我们倾向于使用#if。

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

一致性比任何一种选择都重要,所以我建议你和你的团队一起选择一种风格并坚持下去。

我自己更喜欢:

#if defined(DEBUG_ENABLED)

因为它更容易创建寻找相反条件的代码更容易发现:

#if !defined(DEBUG_ENABLED)

VS

#ifndef(DEBUG_ENABLED)

这是一种风格问题。但我建议采用更简洁的方法:

#ifdef USE_DEBUG
#define debug_print printf
#else
#define debug_print
#endif

debug_print("i=%d\n", i);

您执行此操作一次,然后始终使用debug_print()进行打印或不执行任何操作。 (是的,这将在两种情况下编译。)这样,您的代码不会被预处理器指令乱码。

如果你收到警告“表达无效”并希望摆脱它,这是另一种选择:

void dummy(const char*, ...)
{}

#ifdef USE_DEBUG
#define debug_print printf
#else
#define debug_print dummy
#endif

debug_print("i=%d\n", i);

#if 为您提供了将其设置为0以关闭功能的选项,同时仍然检测到交换机在那里。
我个人总是 #define DEBUG 1 所以我可以用#if或#ifdef来抓住它

#if和#define MY_MACRO(0)

使用#if表示您创建了“定义”宏,即将在代码中搜索的内容将被“(0)”替换。这是“宏观地狱”。我讨厌在C ++中看到它,因为它会通过潜在的代码修改来污染代码。

例如:

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

一个错误。

这意味着您的宏成功地与您的C ++代码交互:对该函数的调用是成功的。在这个简单的例子中,它很有趣。但是我自己使用我的代码默默播放宏的经验并没有充满喜悦和充实,所以......

#ifdef和#define MY_MACRO

使用#ifdef意味着你“定义”一些东西。不是你给它一个值。它仍然是污染性的,但至少它将被“无任何替换”,并且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 ===|

因此...

结论

我宁愿在我的代码中没有宏,但由于多种原因(定义标题保护或调试宏),我不能。

但至少,我喜欢用合法的C ++代码使它们成为最不可能的交互式。这意味着使用#define没有价值,使用#ifdef和#ifndef(甚至是按照Jim Buck建议定义的#if),最重要的是,给他们的名字这么长,所以外星人的头脑中没有人会使用它“偶然”,并且它决不会影响合法的C ++代码。

Post Scriptum

现在,当我重新阅读我的帖子时,我想知道我是否应该尝试找到一些永远不会正确的C ++值添加到我的定义中。像

这样的东西
#define MY_MACRO @@@@@@@@@@@@@@@@@@

可以与#ifdef和#ifndef一起使用,但是如果在函数内部使用则不允许代码编译...我在g ++上成功尝试了这个,并且它给出了错误:

main.cpp|410|error: stray ‘@’ in program|

有趣。 : - )

第一个对我来说更清楚。与定义/未定义相比,它似乎更自然地成为旗帜。

两者完全相同。在惯用语中,#ifdef仅用于检查定义(以及我在示例中使用的内容),而#if用于更复杂的表达式,例如#if defined(A)&& !定义(B)。

有点旧,但使用预处理器打开/关闭日志记录在C ++中肯定是次优的。有很好的日志工具,如Apache的 log4cxx ,它们是开源的,不受限制你如何分发你的申请。它们还允许您在不重新编译的情况下更改日志记录级别,如果您关闭日志记录,则开销非常低,并且您有机会在生产中完全关闭日志记录。

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

#ifdef macro

表示“如果定义了宏”或“如果宏存在”。宏的价值在这里并不重要。它可以是任何东西。

#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 

或者您可以将宏设置为编译器标志。另请查看此处

我曾经使用 #ifdef ,但是当我切换到Doxygen进行文档编制时,我发现无法记录已注释掉的宏(或者,至少Doxygen会产生警告)。这意味着我无法记录当前未启用的功能切换宏。

虽然可以仅为Doxygen定义宏,但这意味着代码的非活动部分中的宏也将被记录。我个人想要显示功能开关,否则只记录当前选择的内容。此外,如果只有在Doxygen处理文件时才需要定义许多宏,它会使代码变得非常混乱。

因此,在这种情况下,最好始终定义宏并使用 #if

我总是使用#ifdef和编译器标志来定义它......

或者,您可以声明一个全局常量,并使用C ++ if而不是预处理器#if。编译器应该为您优化未使用的分支,并且您的代码将更清晰。

以下是 C ++ Gotchas 由Stephen C. Dewhurst谈到使用#if's。

如果为驱动程序指定条件定义的方式不同,则存在差异:

diff <( echo | g++ -DA= -dM -E - ) <( echo | g++ -DA -dM -E - )

输出:

344c344
< #define A 
---
> #define A 1

这意味着, -DA -DA = 1 的同义词,如果省略了值,那么在 #if的情况下可能会导致问题用法。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top