181

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?

moooeeeep
  • 31,622
  • 22
  • 98
  • 187
NebulaFox
  • 7,813
  • 9
  • 47
  • 65
  • 1
    Mostly templates, but it will also make it a bit easier to distribute and use. – BoBTFish Oct 01 '12 at 10:16
  • 13
    I would like to add the downsides of a header-only library to the scope of the question... – moooeeeep Oct 01 '12 at 10:22
  • 1
    What downsides are there that have not already been mentioned? – NebulaFox Oct 01 '12 at 10:36
  • 9
    @moooeeeep: for the downsides, you may want to read the paragraph **"Stop inlining code"** in [C++ Dos and Don'ts](http://dev.chromium.org/developers/coding-style/cpp-dos-and-donts) Chromium Projects web page. – Mr.C64 Oct 01 '12 at 10:41
  • @Mr.C64 link is broken. – ktb Oct 09 '21 at 00:37
  • 1
    @ktb Thanks for the heads up. This is the new link: https://chromium.googlesource.com/chromium/src/+/HEAD/styleguide/c++/c++-dos-and-donts.md – Mr.C64 Oct 10 '21 at 17:51

5 Answers5

101

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.

David Hammen
  • 32,454
  • 9
  • 60
  • 108
  • 40
    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. – Thomas Jun 01 '13 at 12:37
  • 1
    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. – Willem Hengeveld Jul 24 '14 at 12:03
  • 18
    @Thomas - 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. – David Hammen Oct 16 '14 at 19:05
  • 5
    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) – Mark Lakata Feb 18 '15 at 23:53
  • 3
    @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`. – Nemo May 22 '15 at 16:25
  • @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](https://msdn.microsoft.com/en-us/library/hh438477.aspx) member. – mucaho Nov 04 '15 at 18:13
  • 6
    @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. – Joel Cornett Dec 11 '15 at 16:36
  • 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. – Johannes Pille Aug 25 '19 at 19:31
  • 1
    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. – mnr Feb 19 '21 at 14:14
  • "You don't need to build the library", what? Infact, you've to rebuild it every time you compile it. – tripulse Dec 24 '21 at 12:41
88

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.

Luchian Grigore
  • 253,575
  • 64
  • 457
  • 625
  • 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. – James Kanze Oct 01 '12 at 12:29
  • 5
    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. http://stackoverflow.com/questions/12290639/quantifiable-metrics-benchmarks-on-the-usage-of-header-only-c-libraries/13593041#13593041 – Homer6 Nov 28 '12 at 19:35
  • @Homer6 thanks for pinging me. I've never actually measured this. – Luchian Grigore Nov 28 '12 at 19:38
  • 3
    @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. – Homer6 Nov 28 '12 at 19:40
  • @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. – pooya13 May 25 '20 at 18:36
  • 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. – PPP Jul 05 '20 at 06:28
  • @pooya13 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. – Pharap Dec 18 '22 at 19:18
  • @JamesKanze What's the difference between bug report issues in header-only library vs regular library? – NK-cell Jul 26 '23 at 12:12
36

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.

Phil Rosenberg
  • 1,597
  • 1
  • 14
  • 22
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.)

James Kanze
  • 150,581
  • 18
  • 184
  • 329
3

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

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985