【问题标题】:Header-only library design - include directives仅标头库设计 - 包含指令
【发布时间】:2014-07-09 01:15:14
【问题描述】:

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

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


方法 A:

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

// 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:

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

// 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) 中停止工作,在没有包含指令的实现文件中。


编辑:

这个问题被投票关闭,原因是“基于意见”。我不同意,因为在像我的包含文件的库这样的大型仅标头项目中,可能需要大量编译时间。因此,方法 A 可能比方法 B 更快,或者相反。

【问题讨论】:

  • 就我个人而言,我想避免编译器识别重复包含的工作。当头文件是自依赖的时,它们也变得更容易测试,再次减少了开发时间。但赞成这只是我个人的看法。如果您可以定义您的“最佳”标准,那么您就会得到答案。所以本质上,您是在要求其他人定义“最佳”对您意味着什么。
  • 重复 Alf 的建议,每个文件都应该包含它所依赖的所有标题。如果他们愿意,这将允许您的用户对拉入库的特定部分进行细粒度控制,而不是包括 convenience 标题 module.hpp。除非您提供一些证据,否则关于 my large libraryincludes take time 等的最后一次编辑毫无意义。
  • 以防万一这没有被重新打开:正如 Alf 和 Praetorian 建议的那样,我也非常喜欢案例 B,因为它更容易验证一个健全的构建过程(例如 CMake 有一个独立的标头测试宏) .最终用户的模块头当然是个好主意,但您不必担心现代编译器的重复包含。
  • 有了标头保护和#pragma once,我相信同一文件的其他包含不会浪费超过几毫秒的编译时间。

标签: c++ c++11 include header-only


【解决方案1】:

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

  • 现代 IDE 可以使用 libclang 或专有解决方案来解析 #include 指令并提供代码感知语法突出显示和自动完成功能。

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

  • 正如 Alf 所说,让每个文件都包含它所依赖的所有头文件是一种很好的做法 - 库的用户可以“挑选”他们需要的头文件,而不是意外地强制手动包括一个父标题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-10-15
    相关资源
    最近更新 更多