25

Obviously template libraries need to be header only, but for non-templates, when should you make things header-only?

TemplateRex
  • 69,038
  • 19
  • 164
  • 304
Billy ONeal
  • 104,103
  • 58
  • 317
  • 552

4 Answers4

14

If you think your non-template library could be header-only, consider dividing it into two files anyway, then providing a third file that includes both the .h and the .cpp (with an include guard).

Then anyone who uses your library in a lot of different TUs, and suspects that this might be costing a lot of compile time, can easily make the change to test it.

Once you know users have the option which way to use the library, the answer probably becomes "offer that option whenever you possibly can". So pretty much any time that including it from multiple TUs wouldn't violate the ODR. For instance, if your non-static free functions refer to static globals, then you're out of luck, since the different definitions of that function in different TUs would refer to different objects by the same name, which is an ODR-violation.

Steve Jessop
  • 273,490
  • 39
  • 460
  • 699
  • 2
    Oooh. Never thought of doing it this way. +1 How do you get the function declarations to be set to `inline` correctly when the `.cpp` is `#include`d? – Billy ONeal May 28 '11 at 22:54
  • 2
    @Billy: Set a `#define` in the third file (second .h), and in the .cpp have an `#ifdef _____ #define DO_INLINE inline #else #define DO_INLINE #endif`. – Xeo May 28 '11 at 22:55
  • @Steve: That works? I thought if they were declared `inline` then the definition had to exist in the translation unit somewhere. – Billy ONeal May 28 '11 at 22:58
  • @Billy ONeal: Nope. `inline` functions can be safely both declared and defined inline, and then they're done. – Puppy May 28 '11 at 23:03
  • 3
    @Billy, @DeadMG: sorry, Billy's right, I was thinking exactly the same thing as DeadMG when I wrote my comment, but of course it only applies in the header-only case. You do need a "THE_RIGHT_LINKAGE_DAMMIT" macro. – Steve Jessop May 28 '11 at 23:08
  • @DeadMG: @BillyONeal is correct. `inline` functions must be defined in every translation unit in which they are used. This means that if you are in non-"header only" mode `inline` functions must still be defined in every translation unit in which they are used. If they are part of the API then it effectively means their definition has to remain in a header file for the library. – CB Bailey May 28 '11 at 23:10
  • 1
    @Billy, @DeadMG: ... and IIRC, if you make THE_RIGHT_LINKAGE_DAMMIT `static inline` in the header-only case then you can safely combine TUs that use the library in different ways, but you probably can't correctly use local static variables in the function. If it's just `inline` then they're incompatible with the non-inline version, so it has to be an executable-wide decision which to use. It's a while since I messed with this, there are some complications. – Steve Jessop May 28 '11 at 23:14
3

You could follow Boost.Asio lead.

They simply provide the two versions of the libraries: header-only and header + library.

They do it with a single macro to be defined (or not) before including their headers. I think the default (if not defined) is to use the header-only version.

See Optional Separate Compilation.

Note how they neatly provide a single source file to be compiled that define everything or the option to link against a dynamically loaded library.

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
0

Template libraries need not to be header-only: implementations might well contain some pieces independent of template parameters, and for some reasons (e.g. less code size) separated into a special binary.

I cannot imagine a case where a non-template library really must be header-only. However sometimes it might be reasonable performance-wise to allow inlining of all the code. An example can be a library of wrappers around platform-specific interfaces, e.g. for things like synchronization primitives, thread-local storage, platform- and compiler-specific implementation of atomic operations etc.

Alexey Kukanov
  • 12,479
  • 2
  • 36
  • 55
  • Alexey: Show me one library of the STL that is **not** header-only. And with STL, I mean the container/algorithms/iterators part, not the whole standard library. – Xeo May 28 '11 at 23:04
  • Perhaps, but the template parts of the template libraries must be header only. A template definition needs to exist in the translation unit so that the compiler can instantiate it. Some STLs have binary parts mostly because either A. they're included as part of a general C++ runtime which has nontemplate pieces (e.g. `libstdcxx`), or B. they contain components which are not templates. As for the performance thing, most compilers nowadays have link time code generation which pretty much eliminates a performance difference between the two for release builds. – Billy ONeal May 28 '11 at 23:04
  • @Xeo: ok, you are probably correct, STL is not the same as the language support library. – Alexey Kukanov May 28 '11 at 23:09
  • 2
    There is usually some confusion about what the standard library and the STL are. By the original definition of STL (Standard Template Library), the STL is always header only. Then the standard library does contain some other libraries that are not header only. – David Rodríguez - dribeas May 28 '11 at 23:13
  • @Billy: of course you are right about the templates; I clarified what I meant. As for performance: I agree, but you kind of rely on a compiler & linker for that; for the case I described, I personally would prefer to keep everything in headers. – Alexey Kukanov May 28 '11 at 23:20
-2

Without templates, you'd have actual definitions in the headers. That means that if two files include your header, you'd get multiple definitions and the code will not compile.

In other words, putting definitions in headers is a very bad idea. You should stick to declarations only, and templates.

As for templates, compilers know that you may include the same header more than once, they will not generate the same code over and over again.

EDIT: If you mean "keep everything inlined", I think this is a very bad approach. The header files become completely unreadable, and any change in implementation forces any user of your library to recompile everything.

Omri Barel
  • 9,182
  • 3
  • 29
  • 22
  • -1: This is not true. You can put definitions in headers so long as they have internal linkage. (e.g. they're declared `inline`) – Billy ONeal May 28 '11 at 22:56
  • Sometimes, the need to recompile for every change isn't such a limitation. – Dennis Zickefoose May 29 '11 at 03:18
  • @Dennis: True. But sometimes it's better to be able to provide a new shared object that fixes a security issue, rather than to force you to recompile your entire /usr/bin. If there's an exploitable problem in glibc, you can simply update it. If there's an exploitable in STL, you'll have to recompile everything that used it. Not a problem if you're running Gentoo, but I don't. – Omri Barel May 29 '11 at 10:58