Benefits of header-only libraries
Asked Answered
O

5

189

What are the benefits of a header only library and why would you write it that way oppose to putting the implementation into separate file?

Opposite answered 1/10, 2012 at 10:15 Comment(6)
Mostly templates, but it will also make it a bit easier to distribute and use.Joyajoyan
I would like to add the downsides of a header-only library to the scope of the question...Ailey
What downsides are there that have not already been mentioned?Opposite
@moooeeeep: for the downsides, you may want to read the paragraph "Stop inlining code" in C++ Dos and Don'ts Chromium Projects web page.Nieshanieto
@Nieshanieto link is broken.Circularize
@ktb Thanks for the heads up. This is the new link: chromium.googlesource.com/chromium/src/+/HEAD/styleguide/c++/…Nieshanieto
P
92

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). 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.

Proxy answered 1/10, 2012 at 10:18 Comment(11)
"Having a header-only library also means you don't have to worry about different platforms where the library might be used": only if you don't have to maintain the library. Otherwise, it's a nightmare, with bug reports that you cannot reproduce or test on the material you have.Gradus
I just asked a similar question about the performance benefits of header only. As you can see, there is no difference in code size. However, the example header-only implementation ran 7% slower. #12291139Catamount
@Catamount thanks for pinging me. I've never actually measured this.Proxy
@LuchianGrigore I couldn't find anyone else that had either. That's why it took a while to answer. There are so many speculative "increases code size" and "memory consumption" comments. I finally have a snapshot of the differences, even if it's only one example.Catamount
@Homer6. Why wouldn't it increase code size? Assuming you create multiple libs that use the header only lib and then your app uses all those libs you would have to have multiple copies as opposed to linking against a single shared library.Wondrous
header only libraries should be considered a crime. Just try to do anything on the OpenVPN3 project. It's almost impossible. That thing is a monster.Elihu
@Wondrous If the libraries are compiled separately then yes, but if they're all being compiled from source then the compiler should be able to realise which libraries are using the same code and avoid any code duplication that would occur under a separate compilation model.Peculiarity
@JamesKanze What's the difference between bug report issues in header-only library vs regular library?Arterio
@JamesKanze "bug reports that you cannot reproduce or test on the material you have." - irreproducible bug report is an automatic WONTFIX bug report (actually, IRREPRODUCIBLE would be a nice addition for bug reporting systems). And 90% of bug reports out there fall under this category regardless of being header-only.Piny
@No-BugsHare The difference is that it might be a real bug, that you'd want to fix.Gradus
@JamesKanze If there is a bug - it can be reported properly (with a repro), no difference if it is against header-only lib or not. And if somebody doesn't care to provide a repro - it won't work regardless of being header-only or not. The only difference I know about is that header-only libs tend to get better code in the long run (they're tested in more contexts than stuff-which-works-only-with-only-specific-CMakeLists, and pity-on-those-who-dares-to-compile-it-somehow-else).Piny
P
105

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.

  • More tangled compilation. You get a lot more dependencies with a header-only library because of those extra #includes needed with a header-only library. 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.

  • 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.

Pavia answered 1/10, 2012 at 13:4 Comment(11)
Last point doesn't really make sense. Any reasonable documentation will include the function declaration, parameters, return values, etc.. and all associated comments. If you have to refer to the header file, the documentation has failed.Orwin
I find that it actually reduces compilation time, since there are much less objects to build. And another advantage you might add, is that the compiler will have a better opportunity to optimize things with header only libraries.Phantasm
@Orwin - Even with the very best of professional libraries, I oftentimes find myself having to resort to reading the "fine" header. In fact, if the so-called "fine" documentation is extracted from the code plus commentary, I typically like reading the headers. The code plus the comments tells me more than does the auto-generated documentation.Pavia
Last point is not valid. Headers are already filled with implementation details in the private members, so it is not like the cpp file hides all the implementation details. In addition, languages like C# are essentially "header only" by design, and the IDE takes care of obscuring details ("folding" them down)Stepper
@Tomas: Agree, the last point is completely bogus. You can easily keep interface and implementation just as separate with header-only libraries; you simply have the interface header #include the implementation details. This is why Boost libraries typically include a subdirectory (and namespace) called detail.Garrido
@MarkLakata I disagree. Carefully designed headers define their template implementations in an external (e.g. tpp) file, as well as their private members through their pimpl member.Repercussion
@Thomas: I disagree. The header file is generally the first place I go to for documentation. If the header is well written, there is often no need for external documentation.Astatic
As for the last point:SomeClassName.h: Make that a regular header, declarations and documentation only, at the very end include "SomeClassNameDefinitions.h". Implementation detail can be reasonably ~hidden~ moved out of the way while sticking to headers.Spumescent
Dear Tomas, there are many libraries with completely failed documentation, but that you still are forced to use. So I think your comment is a bit besides the point.Leatherleaf
"You don't need to build the library", what? Infact, you've to rebuild it every time you compile it.Girl
1. actually, disadvantages which are attributed in the answer to header-only, are disadvantages of inlining. However, one thing is not mentioned about benefits of inlining - performance (and LOTS of it too!). 2. which makes this whole answer a partisan attack on header-only libs. 3. Not to mention that "bigger object files" is completely bogus in 202x (in the times of terabyte SSDs, nobody cares about those extra 100K of space). 4. "Change the implementation of some key function in the library " - well, it is certainly NOT supposed to happen often -> see above re. partisan attack.Piny
P
92

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). 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.

Proxy answered 1/10, 2012 at 10:18 Comment(11)
"Having a header-only library also means you don't have to worry about different platforms where the library might be used": only if you don't have to maintain the library. Otherwise, it's a nightmare, with bug reports that you cannot reproduce or test on the material you have.Gradus
I just asked a similar question about the performance benefits of header only. As you can see, there is no difference in code size. However, the example header-only implementation ran 7% slower. #12291139Catamount
@Catamount thanks for pinging me. I've never actually measured this.Proxy
@LuchianGrigore I couldn't find anyone else that had either. That's why it took a while to answer. There are so many speculative "increases code size" and "memory consumption" comments. I finally have a snapshot of the differences, even if it's only one example.Catamount
@Homer6. Why wouldn't it increase code size? Assuming you create multiple libs that use the header only lib and then your app uses all those libs you would have to have multiple copies as opposed to linking against a single shared library.Wondrous
header only libraries should be considered a crime. Just try to do anything on the OpenVPN3 project. It's almost impossible. That thing is a monster.Elihu
@Wondrous If the libraries are compiled separately then yes, but if they're all being compiled from source then the compiler should be able to realise which libraries are using the same code and avoid any code duplication that would occur under a separate compilation model.Peculiarity
@JamesKanze What's the difference between bug report issues in header-only library vs regular library?Arterio
@JamesKanze "bug reports that you cannot reproduce or test on the material you have." - irreproducible bug report is an automatic WONTFIX bug report (actually, IRREPRODUCIBLE would be a nice addition for bug reporting systems). And 90% of bug reports out there fall under this category regardless of being header-only.Piny
@No-BugsHare The difference is that it might be a real bug, that you'd want to fix.Gradus
@JamesKanze If there is a bug - it can be reported properly (with a repro), no difference if it is against header-only lib or not. And if somebody doesn't care to provide a repro - it won't work regardless of being header-only or not. The only difference I know about is that header-only libs tend to get better code in the long run (they're tested in more contexts than stuff-which-works-only-with-only-specific-CMakeLists, and pity-on-those-who-dares-to-compile-it-somehow-else).Piny
C
38

I know this is an old thread, but nobody has mentioned ABI interfaces or specific compiler issues. 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.

For example compiler vendors often change their implementation of the STL between versions. 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. 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. Everything goes downhill from there... This is why it is recommended not to pass STL objects across library boundaries. The same applies to C Run-Time (CRT) types.

While talking about the CRT, your library and the user's source code generally need to be linked against the same 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. I can't remember which function it was, but for Visual Studio 2015 Microsoft made one CRT function inline. 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. The result was that these libraries needed recompiling with 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. 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. 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 (e.g an Eigen vector or a GSL matrix). If the 3rd party library changes their header between you compiling your library and your user compiling their code then things will break.

Basically to be safe the only things you can pass across library boundaries are built in types and Plain Old Data (POD). Ideally any POD should be in structs that are defined in your own headers and do not rely on any third party headers.

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).

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.

Calcimine answered 29/9, 2016 at 9:59 Comment(0)
G
13

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.)

Gradus answered 1/10, 2012 at 12:27 Comment(0)
A
4

Inlining can be done by Link Time Optimization (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, which can slow down your compilation times in build systems that automatically rebuild includers.

LTO might have its own downsides too however: Is there a reason why not to use link-time optimization (LTO)?

Acey answered 28/8, 2020 at 16:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.