【发布时间】:2017-07-09 10:53:42
【问题描述】:
我在 CMake 项目中重用 autotools 项目中的一些 C/C++ 源文件,我看到许多源文件中到处都是这样的行:
#ifdef HAVE_UNISTD_H
#include <unistd.h> // for getpid()
#endif
如果getpid() 是可选的并且它的调用被等效的HAVE_UNISTD_H 指令包围,我会理解这个构造的目的。但是,如果没有HAVE_UNISTD_H,源文件将无法编译,抱怨getpid() 未定义。这感觉比编译器让我知道没有找到unistd.h 要神秘得多。
当然,这只是一个例子。其他流行的宏包括HAVE_STDINT_H、HAVE_INTTYPES_H 等,它们的存在是编译源文件所必需的。
为什么要包含HAVE_* 守卫?我觉得它们只会带来缺点:
- 重用此类源文件需要确保存在正确的头文件并且定义了正确的
HAVE_*宏。 - 如果出现错误,开发人员会收到一条更神秘的消息,即编译器不会报告根本原因(未找到标头),而是报告辅助错误(未找到类型/函数)。
- 源文件有点长,阅读起来有点乏味,即
#includes 与#ifdefs 混合。
【问题讨论】:
-
这是 C 还是 C++?大多数当前编译器的默认 C 模式不会发出关于未声明
getpid()的错误消息。这既是支持和反对宏的原因,取决于你问谁。 -
@hvd:请注意,从版本 5 开始的 GCC 默认为 C11 模式,并且至少会为隐式声明的函数生成警告消息。只有旧版本的 GCC 默认为 C90。
-
@JonathanLeffler 我很清楚这一点,这与我的评论并不矛盾。在 GCC 5、6 和 7 中,它仍然只是一个警告。
-
请注意,只执行
#ifdef HAVE_UNISTD_H / #include <unistd.h> / #endif而不提供任何回退的代码违背了HAVE_UNISTD_H检查的意图。代码应该为缺少的内容提供一个通用声明——例如int getpid(void);。否则检查是没有意义的。这就是初衷。在 C90 时代,您可以使用隐式函数声明来摆脱困境——但如果只有 MS 实际实现了 C99 或 C11 或两者,C90 应该早就被遗忘了。