在公共头文件中包含条件是不是被认为是一种好习惯?

Posted

技术标签:

【中文标题】在公共头文件中包含条件是不是被认为是一种好习惯?【英文标题】:is it considered good practice to have conditionals in public header files?在公共头文件中包含条件是否被认为是一种好习惯? 【发布时间】:2012-03-09 18:34:02 【问题描述】:

我的 C 库有一些可选功能,使用 automake,用户可以通过提供标志来配置来打开和关闭它们。

如果关闭某个功能,则不会编译该功能。

但是,我的问题是,在这种情况下,我是否还应该从公共标头中删除函数原型?

为未编译的函数设置函数原型似乎不是一个好主意,但在我看来,根据库配置安装不同的公共头文件似乎不是一个好主意。 (类似于在公共标头目录中安装 config.h 是一种不好的做法。)

对于可选功能,公共标头的最佳方法是什么?如果用户尝试使用禁用的功能,错误应该出现在编译时还是链接时?这种情况必须有一个标准的做法。 (如果有多种想法,我更愿意遵守 GNU 编码标准,但我不知道 GNU 标准在这个问题上。)

【问题讨论】:

我认为如果未定义函数,最好不要包含原型,以便在编译时而不是链接时发现错误,但我不知道任何这方面的标准做法。我会使用#if。 @VaughnCato 如果原型被预处理器指令排除,您将收到编译器错误。 @LuchianGrigore 是的,完全正确。 @LuchianGrigore 是的,这正是重点:如果你让预处理器排除了实现,还要去掉声明,这样人们就会得到编译器错误而不是链接器错误(也可能是动态链接!)。 删除是什么意思?实际上删除文本?我会使用#if 来有效地删除它,而不是实际删除文本。 【参考方案1】:

明确地不仅要从编译中排除实现,还要从整个函数中排除。

//feature.h
#ifndef EXCLUDE_FEATURE
   void foo();
#endif

//feature.c
#include "feature.h"
#ifndef EXCLUDE_FEATURE
void foo()


#endif

这样,如果您尝试使用 foo 并排除该功能,您将得到一个编译器,而不是链接器错误。您希望尽快发出错误信号,链接器错误通常较少传达开发人员的意图。

Microsoft 为 MFC 执行此操作(头文件中的条件),并且效果非常好。 (当然那是 C++,但原则是不变的。

【讨论】:

好的,谢谢。我想到了这一点,我唯一担心的是它似乎与我非常相似,例如包含 config.h 文件——我的印象是特定的库配置不一定反映在标题中,因为例如可以安装多个版本的库。这真的是我对这种方法唯一关心的问题。【参考方案2】:

我认为这个问题有两种有效的方法

拥有一个使用#ifdef 删除某些配置中不支持的功能的头文件 有多个没有#ifdef 的头文件,每个头文件都特定于配置

将 lib 中不存在的函数保留在给定配置的头文件中似乎是一种非常糟糕的做法。它将采取应该是编译时错误并将其移动到链接器的错误。

【讨论】:

我不喜欢第二个建议,您可能会不小心手动包含文件。 @LuchianGrigore 我同意这不是我的偏好。我确实认为这是将它们全部放在一个文件中的一个步骤,尽管没有#ifdefs【参考方案3】:

我在一些项目中观察到以下方法:从模板生成头文件。

文件生成基于配置标志。

在我看来,这种方法比在标题中使用永无止境的条件定义更干净......对我来说,它似乎更干净。

缺点:可能是支持负担(对于脚本)。

【讨论】:

以上是关于在公共头文件中包含条件是不是被认为是一种好习惯?的主要内容,如果未能解决你的问题,请参考以下文章

为啥捕获 RuntimeException 不被认为是一种好的编程习惯? [关闭]

在动态框架中包含静态库的公共头文件

使用 if(...) 时,为啥这被认为是一种好的编程习惯? [复制]

在同一个类中模拟其他公共方法是一种好习惯

如何在某些头文件(嵌套类)的 cpp 文件中包含我的实现

在“for”循环条件中使用“三元运算”是一种好习惯吗?