简体   繁体   English

仅头文件库的好处

[英]Benefits of header-only libraries

仅标头库的好处是什么,为什么要以这种方式编写它而不是将实现放入单独的文件中?

Benefits of header-only library:仅标头库的好处:

  • Simplifies the build process.简化构建过程。 You don't need to build the library, and you don't need to specify the compiled library during the link step of the build.您不需要构建库,也不需要在构建的链接步骤中指定编译的库。 If you do have a compiled library, you will probably want to build multiple versions of it: One compiled with debugging enabled, another with optimization enabled, and possibly yet another stripped of symbols.如果你有一个编译库,你可能想要构建它的多个版本:一个编译启用调试,另一个启用优化,可能还有另一个剥离符号。 And maybe even more for a multi-platform system.对于多平台系统,甚至更多。

Disadvantages of a header-only library:仅标头库的缺点:

  • Bigger object files.更大的目标文件。 Every inline method from the library that is used in some source file will also get a weak symbol, out-of-line definition in the compiled object file for that source file.在某个源文件中使用的库中的每个内联方法也将获得一个弱符号,即该源文件的编译目标文件中的外联定义。 This slows down the compiler and also slows down the linker.这会减慢编译器的速度,也会减慢链接器的速度。 The compiler has to generate all that bloat, and then linker has to filter it out.编译器必须生成所有这些膨胀,然后链接器必须过滤掉它。

  • Longer compilation.更长的编译。 In addition to the bloat problem mentioned above, the compilation will take longer because the headers are inherently larger with a header-only library than a compiled library.除了上面提到的膨胀问题之外,编译将花费更长的时间,因为与编译库相比,仅头文件库的头文件本质上更大。 Those big headers are going to need to be parsed for each source file that uses the library.需要为使用该库的每个源文件解析这些大头文件。 Another factor is that those header files in a header-only library have to #include headers needed by the inline definitions as well as the headers that would be needed had the library been built as a compiled library.另一个因素是,仅头文件库中的头文件必须#include内联定义所需的头文件,以及将库构建为编译库时所需的头文件。

  • More tangled compilation.比较纠结的编译。 You get a lot more dependencies with a header-only library because of those extra #include s needed with a header-only library.由于只包含头文件的库需要额外的#include因此使用只包含头文件的库会获得更多依赖项。 Change the implementation of some key function in the library and you might well need to recompile the entire project.更改库中某些关键函数的实现,您可能需要重新编译整个项目。 Make that change in the source file for a compiled library and all you have to do is recompile that one library source file, update the compiled library with that new .o file, and relink the application.在已编译库的源文件中进行更改,您所要做的就是重新编译该库源文件,使用新的 .o 文件更新已编译的库,然后重新链接应用程序。

  • Harder for the human to read.人类更难阅读。 Even with the best documentation, users of a library oftentimes have to resort to reading the headers for the library.即使有最好的文档,库的用户也经常不得不阅读库的标题。 The headers in a header-only library are filled with implementation details that get in the way of understanding the interface.仅标头库中的标头充满了妨碍理解接口的实现细节。 With a compiled library, all you see is the interface and a brief commentary on what the implementation does, and that's usually all you want.使用编译后的库,您所看到的只是接口和对实现功能的简要说明,这通常就是您想要的。 That's really all you should want.这才是你真正想要的。 You shouldn't have to know implementation details to know how to use the library.您不必了解实现细节即可了解如何使用该库。

There are situations when a header-only library is the only option, for example when dealing with templates.在某些情况下,只有头文件库是唯一的选择,例如在处理模板时。

Having a header-only library also means you don't have to worry about different platforms where the library might be used.拥有仅头文件的库还意味着您不必担心可能使用该库的不同平台。 When you separate the implementation, you usually do so to hide implementation details, and distribute the library as a combination of headers and libraries ( lib , dll 's or .so files).分离实现时,通常这样做是为了隐藏实现细节,并将库作为标头和库( libdll.so文件)的组合进行分发。 These of course have to be compiled for all different operating systems/versions you offer support.这些当然必须针对您提供支持的所有不同操作系统/版本进行编译。

You could also distribute the implementation files, but that would mean an extra step for the user - compiling your library before using it.您也可以分发实现文件,但这对用户来说意味着一个额外的步骤 - 在使用它之前编译您的库。

Of course, this applies on a case-by-case basis.当然,这适用于具体情况 For example, header-only libraries sometimes increase例如,仅标头的库有时会增加code size &代码大小 & compilation times.编译时间。

I know this is an old thread, but nobody has mentioned ABI interfaces or specific compiler issues.我知道这是一个旧线程,但没有人提到 ABI 接口或特定的编译器问题。 So I thought I would.所以我想我会的。

This is basically based on the concept of you either writing a library with a header to distribute to people or reuse yourself vs having everything in a header.这基本上基于这样一个概念:要么编写一个带有标题的库以分发给人们,要么重用自己而不是将所有内容都放在标题中。 If you are thinking of reusing a header and source files and recompiling these in every project then this doesn't really apply.如果您正在考虑重用头文件和源文件并在每个项目中重新编译它们,那么这实际上并不适用。

Basically if you compile your C++ code and build a library with one compiler then the user tries to use that library with a different compiler or a different version of the same compiler then you may get linker errors or strange runtime behaviour due to binary incompatibility.基本上,如果您编译 C++ 代码并使用一个编译器构建一个库,那么用户尝试使用不同的编译器或同一编译器的不同版本使用该库,那么由于二进制不兼容,您可能会遇到链接器错误或奇怪的运行时行为。

For example compiler vendors often change their implementation of the STL between versions.例如,编译器供应商经常在版本之间更改他们的 STL 实现。 If you have a function in a library that accepts a std::vector then it expects the bytes in that class to be arranged in the way they were arranged when the library was compiled.如果库中有一个接受 std::vector 的函数,那么它期望该类中的字节按照编译库时的排列方式进行排列。 If, in a new compiler version, the vendor has made efficiency improvements to std::vector then the user's code sees the new class which may have a different structure and passes that new structure into your library.如果在新的编译器版本中,供应商对 std::vector 进行了效率改进,那么用户的代码会看到可能具有不同结构的新类并将该新结构传递到您的库中。 Everything goes downhill from there... This is why it is recommended not to pass STL objects across library boundaries.一切都从那里开始走下坡路......这就是为什么建议不要跨库边界传递 STL 对象。 The same applies to C Run-Time (CRT) types.这同样适用于 C 运行时 (CRT) 类型。

While talking about the CRT, your library and the user's source code generally need to be linked against the same CRT.在谈论 CRT 时,您的库和用户的源代码通常需要针对同一个 CRT 进行链接。 With Visual Studio if you build your library using the Multithreaded CRT, but the user links against the Multithreaded Debug CRT then you will have link problems because your library may not find the symbols it needs.使用 Visual Studio,如果您使用多线程 CRT 构建库,但用户针对多线程调试 CRT 进行链接,那么您将遇到链接问题,因为您的库可能找不到所需的符号。 I can't remember which function it was, but for Visual Studio 2015 Microsoft made one CRT function inline.我不记得它是哪个函数,但是对于 Visual Studio 2015,Microsoft 内联了一个 CRT 函数。 Suddenly it was in the header not the CRT library so libraries that expected to find it at link time no longer could do and this generated link errors.突然,它在标头中而不是 CRT 库中,因此期望在链接时找到它的库不再能做,这会产生链接错误。 The result was that these libraries needed recompiling with Visual Studio 2015.结果是这些库需要使用 Visual Studio 2015 重新编译。

You can also get link errors or strange behaviour if you use the Windows API but you build with different Unicode settings to the library user.如果您使用 Windows API,但您使用不同的 Unicode 设置构建库用户,您也可能会收到链接错误或奇怪的行为。 This is because the Windows API has functions which use either Unicode or ASCII strings and macros/defines which automagically use the correct types based on the project's Unicode settings.这是因为 Windows API 具有使用 Unicode 或 ASCII 字符串和宏/定义的函数,它们根据项目的 Unicode 设置自动使用正确的类型。 If you pass a string across the library boundary that is the wrong type then things break at runtime.如果您跨库边界传递错误类型的字符串,那么事情会在运行时中断。 Or you may find that the program doesn't link in the first place.或者您可能会发现该程序一开始就没有链接。

These things are also true for passing objects/types across library boundaries from other third party libraries (eg an Eigen vector or a GSL matrix).这些事情对于从其他第三方库(例如特征向量或 GSL 矩阵)跨库边界传递对象/类型也是正确的。 If the 3rd party library changes their header between you compiling your library and your user compiling their code then things will break.如果第 3 方库在您编译您的库和您的用户编译他们的代码之间更改了它们的标头,那么事情就会中断。

Basically to be safe the only things you can pass across library boundaries are built in types and Plain Old Data (POD).基本上,为了安全起见,您可以跨库边界传递的唯一内容是内置类型和普通旧数据 (POD)。 Ideally any POD should be in structs that are defined in your own headers and do not rely on any third party headers.理想情况下,任何 POD 都应位于您自己的标头中定义的结构中,并且不依赖于任何第三方标头。

If you provide a header only library then all the code gets compiled with the same compiler settings and against the same headers so a lot of these problems go away (providing the version of third partly libraries you and your user uses are API compatible).如果您提供仅标头库,那么所有代码​​都将使用相同的编译器设置并针对相同的标头进行编译,因此很多这些问题都会消失(前提是您和您的用户使用的第三方库的版本与 API 兼容)。

However there are negatives that have been mentioned above, such as the increased compilation time.然而,上面已经提到了一些负面因素,例如编译时间增加。 Also you may be running a business so you may not want to hand all your source code implementation details to all your users in case one of them steals it.此外,您可能正在经营一家企业,因此您可能不想将所有源代码实现细节交给所有用户,以防他们中的一个人窃取它。

The main "benefit" is that it requires you to deliver source code, so you'll end up with error reports on machines and with compilers you've never heard of.主要的“好处”是它要求您提供源代码,因此您最终会在机器上和您从未听说过的编译器上得到错误报告。 When the library is entirely templates, you don't have much choice, but when you have the choice, header only is usually a poor engineering choice.当库完全是模板时,您没有太多选择,但是当您有选择时,仅标头通常是一个糟糕的工程选择。 (On the other hand, of course, header only means that you don't have to document any integration procedure.) (当然,另一方面,标题仅意味着您不必记录任何集成过程。)

Inlining can be done by Link Time Optimization (LTO)内联可以通过链接时间优化 (LTO) 完成

I'd like to highlight this since it decreases the value of one of the two main advantages of header only libraries: "you need definitions on a header to inline".我想强调这一点,因为它降低了仅标头库的两个主要优势之一的价值:“您需要对标头进行定义以进行内联”。

A minimal concrete example of this is shown at: Link-time optimization and inline一个最小的具体例子显示在: 链接时优化和内联

So you just pass a flag, and inlining can be done across object files without any refactoring work, no need to keep definitions in headers for that anymore.所以你只需要传递一个标志,内联就可以跨对象文件完成,无需任何重构工作,不再需要在头文件中保留定义。

LTO might have its own downsides too however: Is there a reason why not to use link-time optimization (LTO)?然而,LTO 也可能有其自身的缺点: 是否有理由不使用链接时优化 (LTO)?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM