【问题标题】:What is the purpose of HAVE_* macros?HAVE_* 宏的用途是什么?
【发布时间】: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_HHAVE_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 &lt;unistd.h&gt; / #endif 而不提供任何回退的代码违背了HAVE_UNISTD_H 检查的意图。代码应该为缺少的内容提供一个通用声明——例如int getpid(void);。否则检查是没有意义的。这就是初衷。在 C90 时代,您可以使用隐式函数声明来摆脱困境——但如果只有 MS 实际实现了 C99 或 C11 或两者,C90 应该早就被遗忘了。

标签: c++ c macros autotools


【解决方案1】:

大多数HAVE_xxx_h 守卫是 POSIX 出现并标准化头文件之前时代的残余。在 90 年代初期,您可能很容易遇到 确实 具有 getpid(),但没有有效的 unistd.h 的系统 - 该函数将简单地在另一个头文件中声明,或者它不会被声明完全可以,但它仍然可以工作(只要它的返回值是 int 大小的),因为声明在 K&R 和 C89 C 中是可选的。

当时使用的众多系统之间甚至存在更奇怪的问题。例如,有些系统发布了time.h,那些发布了sys/time.h,以及同时发布了两者——除了在最后一个类别中,有一个子集尝试实际包含两者会导致编译错误! Autoconf 的明确设计目标之一是支持大量此类系统,尽可能不提前列出它们,而一些长期不相关的 hack 仍然是carefully documented

除了上述问题之外,在将代码移植到非 POSIX 系统(例如 windows)时,将标头名称与对函数的支持分离可能会很有用。在这样的系统上,posix 头文件可能会丢失或损坏,而实际的函数定义来自诸如 gnulib 之类的可移植库。

【讨论】:

  • “由于声明在 pre-ANSI C 中是可选的”——C89/C90 也是如此。
  • @hvd 谢谢,已更正。 C99和C11呢? Gcc 至少似乎允许使用未声明的函数,但只发出警告。
  • 正式地,在 C99 和 C11 中,这是一个语法错误(请参阅主要表达式,脚注对此很明确),但允许编译器接受语法上无效的代码作为扩展,而 GCC 这样做是为了向后兼容性(我不相信 GCC 是唯一的)。
  • @hvd 当您提到它是一个语法错误时,您可能指的是 ANSI 之前的声明吗?在答案的上下文中,我指的是使用函数without it having been declared at all(但在单独的编译单元中定义)。这样的函数调用不能是语法错误,因为调用的语法是正确的,但仍会被例如拒绝。 C++。
  • 不,这确实被归类为语法错误,我已经提供了参考。函数调用语法要求调用表达式,未声明的标识符不被视为表达式,即使它们看起来很像。
【解决方案2】:

为什么要包含 HAVE_* 守卫?我觉得它们只会带来缺点:...

来源可以采用替代实现。在这种情况下,您当然不希望因缺少包含而出现错误。

1例子:

#ifdef HAVE_UNISTD_H
#include <unistd.h>  // for getpid()
#endif
#ifdef HAVE_WINDoWS_H
#include <windows.h> // for GetProcessId()
#endif

1我知道windows支持getpid()

【讨论】:

    猜你喜欢
    • 2012-09-02
    • 2012-06-03
    • 1970-01-01
    • 1970-01-01
    • 2010-09-27
    • 2011-12-17
    • 2010-11-21
    • 1970-01-01
    相关资源
    最近更新 更多