强制编译器忽略程序中的某些行

53

假设我有一万行C++代码,其中两百行是用于测试目的的(例如,检查程序并显示错误消息)。

在C++中是否有一种方法可以忽略或考虑某些代码行(可能使用预处理器关键字)?


15
#ifdef TESTS 被称为条件编译。 - Grijesh Chauhan
66
看到这个问题的标题时,我立即想到“嗯,将它们注释掉?”;-) - Ajedi32
5
这个问题让我感到关注,因为与其将200行代码嵌入到10000行代码中(可能在很多文件中),将测试代码放在特定的单元测试和/或集成测试文件中会更有意义。此外,生产代码10K行的200行测试代码(2%)很可能意味着您的测试覆盖率非常低。我意识到这不是问题的重点(因此是评论而不是答案),但我认为您应该评估自己的测试策略。 - Dale Wilson
10
经历了这么多年的StackOverflow,我感到非常惊讶,这不是一个重复的问题。 - jpmc26
3
似乎每个人都建议使用#ifdef(或#if defined)。那是一种糟糕的风格,几乎完全错误。应该使用#if而不是#ifdef。原因是#define IS_TEST_BUILD 0应该与#undef IS_TEST_BUILD产生相同的效果,而不是与#define IS_TEST_BUILD 1产生相同的效果。 - Ben Voigt
显示剩余5条评论
8个回答

79

简短回答:

使用#ifdef检查。例如:

#ifdef MY_CONTROL_MACRO
...
#endif

只有在您定义了MY_CONTROL_MACRO宏之后,此作用域中的代码才会被编译。


更多内容:

  1. 要定义这样的宏,您可以:

    • #define MY_CONTROL_MACRO 添加到您的代码中。或
    • 对于VS,请将MY_CONTROL_MACRO添加到项目 > 属性 > C/C++ > 预处理器 > 预处理器定义中。或
    • 对于GCC,请使用选项-DMY_CONTROL_MACRO编译您的代码。
  2. 您可以在此处查看更多信息。

    此块称为条件组。仅当定义MACRO时,控制文本才会包含在预处理器的输出中。如果定义了MACRO,则我们说条件成功;否则失败。

    条件中的受控文本可以包括预处理指令。仅当条件成功时才执行它们。您可以在其他条件组内嵌套条件组,但它们必须完全嵌套。换句话说,“#endif”始终匹配最近的“#ifdef”(或“#ifndef”或“#if”)。此外,您不能在一个文件中开始条件组并在另一个文件中结束。

  3. 您还可以使用高级的ifdef-else-endif样式:

    #ifdef MY_CONTROL_MACRO
        ... // this part will be valid if MY_CONTROL_MACRO is defined
    #else
        ... // this part will be valid if MY_CONTROL_MACRO is NOT defined
    #endif
    

1
我不知道其他编译器,但是VC++在开始调试构建时会自动定义“_DEBUG”。 - Proxy
3
怎么做呢?我猜你可以用 -D_DEBUG 运行 GCC? - You
@herohuyongtao:是的,但这如何使_DEBUG“不可移植”? - You
1
标准宏是NDEBUG,在调试版本中未定义。特别地,定义它会移除assert评估。 - MSalters
4
请使用#if ONLY_FOR_DEBUG,而不是#ifdef - Ben Voigt
显示剩余4条评论

13

在代码周围加上"#ifdef...#endif",然后使用编译器选项来设置标志:

#ifdef MYTEST_ONLY_FUNCTIONALITY_ENABLED
...
#endif
您可以使用编译器选项来包含这段代码。例如,在GCC中:
-DMYTEST_ONLY_FUNCTIONALITY_ENABLED

说实话,我认为这种方法在大型项目中通常不太可维护,如果可能的话,通常最好将仅用于测试的代码移至完全独立的库中(没有这种条件逻辑),并将该代码链接到测试二进制文件而不是非测试二进制文件中。这也避免了需要同时编译每个其他库的调试模式和非调试模式。


2
我认为这种方法可以在代码量增加时简化可维护性,因为控制只需要一个地方。当此类生成开关的数量增加时,它会变得难以维护,而且非常快速。要比宇宙中原子的数量还多,需要大约400个开关才能获得更多的构建组合(是的,我曾经处理过如此复杂的代码库)!然而,即使如此也比绕过注释代码块更易于维护! - Cheeseminer

6
这是#ifdef的设计初衷。
你需要放置:
#ifdef TESTS
... test code ...
#endif

然后你可以传递编译器选项来决定是否编译测试部分。例如,使用g++:

g++ -DTESTS ...

不是很多。这就是#if的设计目的。#ifdef的设计目的是提供默认值,例如#ifndef VALUE / #define VALUE DEFAULT_VALUE / #endif - Ben Voigt

6

使用预处理器保护是最灵活和常见的方法。但是,在可能的情况下,我建议使用if语句。例如,不要使用以下代码:

void example(int a){
   int some_local;
   ...
   #ifdef _DEBUG
   std::cout << "In function " << __FUNCTION__ << "(" << a <<")" << std::endl;
   #endif
   ....
}

假设ENABLE_DEBUG被定义为0或非0,我会使用:
void example(int a){
   int some_local;

   ...
   if(ENABLE_DEBUG) std::cout << "In function " << __FUNCTION__ << "(" << a <<")" << std::endl;
   ...
}

由于ENABLE_DEBUG是一个常量,当ENABLE_DEBUG为0时,编译器将不会为其保护的语句生成任何代码。那么,为什么要使用这种方法而不是#ifdef?

  1. 如果有很多单独的调试语句分散在整个代码中,阅读起来可能会更容易一些。
  2. 更重要的是,即使没有生成任何代码,代码也总是被处理语法错误。如果调试代码不经常启用,则这非常有帮助。如果变量发生变化(例如在上面的示例中,如果参数a被重命名),则进行更改的人将知道他们必须更新调试语句。如果使用#ifdef,则它可能会隐藏比特腐败,直到有人需要启用调试代码,然后他们必须去尝试修复代码,这对他们来说可能并不明显。

显然,此方法仅适用于方法/函数体内的调试语句。


这是非常好的建议 - 确保您的调试代码经过检查是一个好主意! - Daniel Buckmaster

3

按照现有惯例,使用NDEBUG宏。所有常见的编译器都为发布版定义了此宏,并且未为调试版本定义此宏。

该宏最初存在是用于控制assert(3)的输出,并且自 POSIX标准以来至少从C89开始就定义了该宏。

请注意,您必须使用#ifndef反转测试。

一个例子:

#ifndef NDEBUG
    /* Debugging code */
    std::cerr << "So far we have seen " << unicorns << " unicorns" << std::endl;
#endif

附言:使用gcc/g++,您可以通过在命令行中添加-g来进行调试构建。


2
使用预处理器的#define和#if
根据您的编译器,您应该默认可用一些变量,例如NDEBUG(表示非调试)或DEBUG。
您还可以通过代码自己定义变量。
#define MY_VARIABLE

并按以下方式使用

#ifdef MY_VARIABLE
  //code that compiles only if MY_VARIABLE is defined
  printf("test output here");
#else
  //code that compiles only if MY_VARIABLE is NOT defined
  printf("MY_VARIABLE is not defined");
#endif

欲了解更多信息,请在网上搜索

#define, #if, #ifdef, #ifndef

2

使用预处理器指令,将define传递给编译器或从头文件"config.h"中获取是正确的方法:

#if defined(DEBUG) // or #ifdef DEBUG
    // Debug code
#endif

为了避免在源代码中到处使用:
#if defined(DEBUG)
    My_Debug_function(some_variable)
#endif

您可以在标题中执行以下操作。
#if !defined(DEBUG) // or #ifndef DEBUG
# define My_Debug_function(some_variable) do { static_cast<void>(some_variable); } while (false)  /* Do nothing */
#endif

因此,几乎可以正常使用My_Debug_function


2

在你的测试代码中加上#ifdef DEBUG

#if DEBUG
   ....
#endif

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接