仅头文件库设计-包含指令

Header-only library design - include directives

本文关键字:-包 指令 文件      更新时间:2023-10-16

我正在创建一个仅头文件的c++ 11/14库,我不确定我应该如何处理库文件之间的#include指令。

我应该尝试在面向用户的模块头文件中尽可能多地分组#include指令,还是应该内部文件包括他们需要的文件(有时重复相同的包括)?


方法:

在这种方法中,模块头文件包括所有必需的依赖项,然后包括实现。实现头文件本身不包含任何内容。

// Library/Module/Module.hpp
// This file is intended to be included by the user in his projects.
#ifndef MODULE
#define MODULE
#include <vector>
#include "Library/Module/Impl/SharedDependency.hpp"
#include "Library/Module/Impl/Class1.hpp"
#include "Library/Module/Impl/Class2.hpp"
#endif MODULE

// Library/Module/Impl/SharedDependency.hpp
#ifndef SHARED_DEPENDENCY
#define SHARED_DEPENDENCY
inline void sharedFunc() { }
#endif

// Library/Module/Impl/Class1.hpp
#ifndef CLASS1
#define CLASS1
// No need to include "SharedDependency.hpp", as it will be included by
// the module header file. Same applies for <vector>.
struct Class1 
{ 
    std::vector<int> v;        
    Class1() { sharedFunc(); } 
};
#endif

// Library/Module/Impl/Class2.hpp
#ifndef CLASS2
#define CLASS2
// No need to include "SharedDependency.hpp", as it will be included by
// the module header file. Same applies for <vector>.
struct Class2
{ 
    std::vector<int> v;        
    Class2() { sharedFunc(); } 
};
#endif


方法B:

在这种方法中,模块头文件只包含实现头文件。如果实现头文件需要额外的include,它们会包含文件本身(递归地),有时会重复相同的include。

// Library/Module/Module.hpp
// This file is intended to be included by the user in his projects.
#ifndef MODULE
#define MODULE
#include "Library/Module/Impl/Class1.hpp"
#include "Library/Module/Impl/Class2.hpp"
#endif MODULE

// Library/Module/Impl/SharedDependency.hpp
#ifndef SHARED_DEPENDENCY
#define SHARED_DEPENDENCY
inline void sharedFunc() { }
#endif

// Library/Module/Impl/Class1.hpp
#ifndef CLASS1
#define CLASS1
#include <vector>
#include "Library/Module/Impl/SharedDependency.hpp"
struct Class1
{ 
    std::vector<int> v;        
    Class1() { sharedFunc(); } 
};
#endif

// Library/Module/Impl/Class2.hpp
#ifndef CLASS2
#define CLASS2
#include <vector>
#include "Library/Module/Impl/SharedDependency.hpp"
struct Class2
{ 
    std::vector<int> v;        
    Class2() { sharedFunc(); } 
};
#endif

什么是最好的方法?

直觉上,我认为方法A是最好的,因为它避免了重复相同的包含,并明确了哪些文件需要在其他文件之前包含。然而,最大的缺点是,语法高亮在我的IDE (QT-Creator)中停止工作,在没有include指令的实现文件中。


编辑:

由于"基于意见"的原因,该问题被投票关闭。我不同意,因为在像我的库这样的大型头文件项目中,包含文件可能会花费大量的编译时间。因此,方法A可能比方法B快,或者相反。

方法B实际上是最好的方法,因为多次包含相同的头文件不会产生任何可观察到的编译时间增加,但有以下优点:

  • 现代ide可以使用libclang或专有解决方案来解析#include指令,并提供代码感知的语法高亮和自动完成功能。

  • 正如TemplateRex所提到的,验证一个相同的构建过程变得更加容易。例如,CMake提供了为每个头自动生成测试的宏。

  • 正如Alf所提到的,让每个文件包含它所依赖的所有头文件是一种良好的做法-库的用户可以然后"挑选"他们需要的头文件,而不是意外地强制手动包含父头文件。