强制编译器忽略程序中的某些行
Posted
技术标签:
【中文标题】强制编译器忽略程序中的某些行【英文标题】:Force the compiler to ignore some lines in the program 【发布时间】:2014-01-21 11:03:35 【问题描述】:假设我有 10,000 行 C++ 代码。 200 行代码用于测试目的(例如,检查程序并显示错误消息)。
在 C++ 中有没有办法忽略或考虑某些代码行(可能带有 preprocessor 关键字)?
【问题讨论】:
#ifdef TESTS
称为条件编译。
当我看到这个问题的标题时,我立刻想到“呃,把它们注释掉?” ;-)
这个问题与我有关,因为与其将 200 行代码嵌入 10,000 行(可能在很多文件中),不如将测试代码放在特定的单元测试中,和/或集成测试文件。此外,10K 行生产代码的 200 条测试线(2%)很可能意味着您的测试覆盖率非常低。我意识到这不是问题的重点(因此是评论而不是答案),但我认为您应该评估您的测试策略。
在 *** 使用了这么多年之后,这不是重复的,我感到非常惊讶。
每个人似乎都在建议#ifdef
(或#if defined
)。那是糟糕的风格,边界完全被打破。使用#if
,而不是#ifdef
。原因是#define IS_TEST_BUILD 0
应该和#undef IS_TEST_BUILD
一样,而不是#define IS_TEST_BUILD 1
。
【参考方案1】:
简答:
使用宏和#ifdef
检查。例如:
#ifdef MY_CONTROL_MACRO
...
#endif
只有在您已经定义了MY_CONTROL_MACRO
宏时,才会编译此范围内的代码。
更多内容:
要定义这样的宏,你可以
将#define MY_CONTROL_MACRO
添加到您的代码中。或者,
对于 VS,将 MY_CONTROL_MACRO
添加到 Project > Properties > C/C++ > Preprocessor > Preprocessor Definitions
。或者,
对于 GCC,使用选项 -DMY_CONTROL_MACRO
编译您的代码。
您可以查看here 了解更多信息。
这个块被称为条件组。当且仅当定义了 MACRO 时,受控文本才会包含在预处理器的输出中。我们说如果定义了 MACRO 则条件成功,如果没有定义则失败。
条件中的受控文本可以包含预处理指令。它们仅在条件成功时执行。您可以将条件组嵌套在其他条件组中,但它们必须完全嵌套。换句话说,“#endif”总是匹配最近的“#ifdef”(或“#ifndef”或“#if”)。此外,您不能在一个文件中启动条件组并在另一个文件中结束它。
您也可以使用高级的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
【讨论】:
我不知道其他编译器,但是VC++在你开始调试构建时会自动定义_DEBUG
。
@herohuyongtao:怎么会这样?我假设您可以使用 -D_DEBUG
运行 GCC?
@你看here。 -D name
会将名称预定义为宏,定义为 1。
标准宏是NDEBUG
,它未为调试版本定义。特别是,定义它会删除 assert
评估。
使用#if ONLY_FOR_DEBUG
,而不是#ifdef
。【参考方案2】:
用“#ifdef...#endif”包围代码,然后使用编译器选项设置标志:
#ifdef MYTEST_ONLY_FUNCTIONALITY_ENABLED
...
#endif
然后您可以使用编译器选项来包含此代码。例如,在 GCC 中:
-DMYTEST_ONLY_FUNCTIONALITY_ENABLED
不过,老实说,我认为这种方法在大型项目中通常不太易于维护,如果可能的话,最好将仅测试代码简单地移动到一个完全独立的库中(没有这个条件逻辑)和只需将该代码链接到您的测试二进制文件而不是您的非测试二进制文件。这也避免了在调试和非调试模式下编译每个其他库。
【讨论】:
我想说这种方法随着代码量的增加而简化了可维护性,因为控制只是在一个地方。当这种构建开关的数量增加时,它确实变得无法维护并且非常迅速。大约需要 400 次开关才能拥有比宇宙中的原子更多的构建组合(是的,我曾研究过如此复杂的代码库)!然而,即使这样也比在评论代码块中进出更容易维护!【参考方案3】:这就是 #ifdef
的设计目的
你放
#ifdef TESTS
... test code ...
#endif
然后您可以传递给编译器选项来决定是否要编译测试部分。例如使用 g++ 它是
g++ -DTESTS ...
【讨论】:
没那么多。这就是#if
的设计目的。 #ifdef
旨在提供默认值,如 #ifndef VALUE
/ #define VALUE DEFAULT_VALUE
/ #endif
【参考方案4】:
使用预处理器保护绝对是最灵活和最常用的方法。但是,如果可能,我建议使用 if 语句。例如,而不是
void example(int a)
int some_local;
...
#ifdef _DEBUG
std::cout << "In function " << __FUNCTION__ << "(" << a <<")" << std::endl;
#endif
....
假设 ENABLE_DEBUG 被定义为 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?
-
如果在整个代码中散布着许多单独的调试语句,阅读起来会更容易一些
更重要的是,即使没有生成代码,代码也会始终针对语法错误进行处理。如果不经常启用调试代码,这将非常有用。如果变量更改(例如,在上面的示例中,如果参数 a 被重命名),那么进行更改的人将知道他们也必须更新调试语句。如果使用#ifdefs,那么它可以隐藏bit rot,直到有人需要启用调试代码,然后他们必须去尝试修复代码,这对他们来说可能并不明显。
显然这种方法只适用于方法/函数体中的调试语句。
【讨论】:
这是个好建议 - 确保检查您的调试代码是个好主意!【参考方案5】:遵循现有约定,并使用NDEBUG
宏。所有常见的编译器都为 release 构建定义此宏,而不为 debug 构建定义它。
该宏最初是为了控制assert(3)
的输出而存在的,并且一直被定义为in the POSIX standard 并且至少从C89 开始。
请注意,您必须使用 #ifndef
反转测试。
一个例子:
#ifndef NDEBUG
/* Debugging code */
std::cerr << "So far we have seen " << unicorns << " unicorns" << std::endl;
#endif
附:使用gcc
/g++
,您可以通过将-g
添加到命令行来进行调试构建。
【讨论】:
【参考方案6】:包围你的测试代码#ifdef DEBUG
。
#if DEBUG
....
#endif
【讨论】:
【参考方案7】:要走的路是使用预处理器指令,将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
。
【讨论】:
【参考方案8】:使用预处理器#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
【讨论】:
以上是关于强制编译器忽略程序中的某些行的主要内容,如果未能解决你的问题,请参考以下文章