假设我正在编写一个只包含标题或主要包含标题的库,并具有以下代码:
using my_type = int;
namespace detail {
inline void foo() { my_type x; do_foo_stuff(x); }
}
inline void bar() { do_bar_stuff(); detail::foo(); }
inline void baz(my_type y) { do_baz_stuff(y); detail::foo(); }
我想把foo()
放在另一个文件中。我的动机是我有很多这样的详细和不详细的函数,我希望我的公共API的头文件不会被detail
中出现的内容弄得一团糟,并且不打算直接使用。
问题是,什么是一个惯用的方法来做到这一点?
- 我不能只在我的公共头的 end 包含一个带有
detail::
代码的文件--因为声明需要在使用它们的时候进行。 - 我不能只在我的公共头的 * 开头 * 包含一个带有
detail::
代码的文件-因为它们依赖于一些公共定义,例如。类型和常量。让我们假设它们不依赖于任何 * 函数 *。
所以不可能是这两个选项之一。
3条答案
按热度按时间ffscu2ro1#
因为通常头文件包含声明,而源文件包含实现,所以人们认为头文件是接口,公共API,而源文件包含实现细节。在库中,用户(库的消费者)看不到源文件的内容,但可以看到头文件,这一事实进一步加强了这一点。
然而,这是错误的:
头文件和源文件的分离不是在公共接口/实现屏障上完成的。这种代码分离只是C及其C遗产的设计方式的一个产物。C不需要多遍编译器,所以它需要在使用前声明和一个定义。所以header是一个解决方案。它们不是API规范。
因此,不幸的结论是,C没有API /实现分离,试图使用头将是不成功的。
现在回答你的问题:我知道的惯用方法确实是使用
details
或impl
名称空间。可以理解,以这种方式命名的名称空间包含库实现细节,不应该在用户代码中使用。我个人不会改变你最初的设计。C20终于引入了模块,这解决了这个问题。现在我们有了一个清晰的内部符号分离,这些符号在消费者和公共API中是看不到的。
vatpfxk52#
如果你不要求“public API”实现在main header中,并且只对函数签名感到满意,你可以将声明拆分到main header中,将定义拆分到detail header中,如下所示:
使用gcc 11.3编译
rkkpypqq3#
我目前的想法是对同一个头文件进行文件开始和文件结束包含,而不是给它一个包含保护,使用两次包含机制,第一次包含暴露声明,第二次暴露定义,例如。比如:
但这看起来很做作,我还没看到有人用过。