必须仅包含在调试版本中的源文件的条件编译

Conditional compilation of a source file that must be included only in debug builds

我一直在阅读条件编译的最佳实践,但我还没有找到适合我的情况的示例。

我有一个 C 项目,其目标平台是特定设备(不同于 PC)。我有一个源文件,它只包含用于集成测试和类似功能的功能。我希望此文件仅在 DEBUG 版本中编译和链接,而不是在 RELEASE 版本中编译和链接。

我的问题是以下哪个选项更好:

一个*.c文件如下:

Tests.c
---------
#ifndef NDEBUG
// All testing functions
...
#endif

并将该文件包含在 DEBUGRELEASE 版本中。

检查是否从项目的 Makefile / CMakeLists.txt 中定义了 NDEBUG 并因此包括提到的源文件。

我个人对此的看法 (!):

第一种方法 -- #ifndef NDEBUG -- 更可取。

一开始是cc *.c

然后是添加适当的选项。

然后是构建系统,它找出那些 *.c 文件中的哪些实际上 需要 重新编译,并且让您不必记住哪些适当的选项 .

然后出现了更复杂的构建系统,它可以为您找出合适的选项。

随着时间的推移,构建系统变得更加智能,并且可以保持重要的逻辑。然而,我觉得这种智能应该继续专注于它们的主要功能(见上文),而且 - 最后 - cc *.c 应该仍然在做它的工作。

构建系统过时或被替换。下一个人可能甚至不知道您选择的构建系统;他应该仍然能够从您的项目中得出结论,而不必深入了解您的构建系统的逻辑。

设置/检查 NDEBUG 是 C,任何熟悉该语言(和 <assert.h>)的人都会立即认出您打算在那里做什么。

从您的构建系统中弄清楚为什么特定源文件只应包含在特定构建类型中而不应包含在其他构建类型中并不是那么直观,并且当有人站出来扔掉您的 CMakeLists.txt 因为他更喜欢 Jam 并且从头开始构建它。那个人可能最终想知道为什么所有这些测试都会弄乱他的发布代码,以及为什么你不够聪明来制作它们 Debug-only(没有意识到你 确实 这样做了在你的构建系统中)。

我认为第一种方法 (#ifndef NDEBUG) 更好,为什么?

因为我认为每一个代码的封装或者依赖都应该在尽可能低的层次上进行。如果构建系统可以在不知道此文件仅为 DEBUG 构建编译的情况下继续其工作,那么我们刚刚成功删除了项目中的依赖项。

在最后一个参数的基础上,如果将来其他项目需要此文件,您将有两个有条件行为的地方,而不是一个地方。

其实我不明白为什么只选一个?在我看来,这两个选项都可以并且应该一起使用。

如果某个文件在发布构建中完全不需要,那么您就有充分的理由将其完全排除在构建过程之外。但是在源代码中有一些预处理器保护(无论是 #ifdef 还是 #error)无论如何都非常有用。