johnbakers
johnbakers

Reputation: 24771

How does this header-only library guard against linker problems?

After reading this question I thought I understood everything, but then I saw this file from a popular header-only library.

The library uses the #ifndef line, but the SO question points out that this is NOT adequate protection against multiple definition errors in multiple TUs.

So one of the following must be true:

  1. It is possible to avoid multiple definition linker errors in ways other than described in the SO question. Perhaps the library is using techniques not mentioned in to the other SO question that are worthy of additional explanation.
  2. The library assumes you won't include its header files in more than translation unit -- this seems fragile since a robust library shouldn't make this assumption on its users.

I'd appreciate having some light shed on this seemingly simple curiosity.

Upvotes: 1

Views: 1403

Answers (5)

Nikita Kakuev
Nikita Kakuev

Reputation: 1136

This header file can indeed be included in difference source files without causing "multiple symbol definition" errors.

This happens because it is fine to have multiple identically named symbols in different object files as long as these symbols are either weak or local.

Let's take a closer look at the header file. It (potentially) defines several objects like this helper:

static int const helper[] = {0,7,8,13};

Each translation unit that includes this header file will have this helper in it. However, there will be no "multiple symbol definition" errors, since helper is static and thus has internal linkage. The symbols created for helpers will be local and linker will just happily put them all in the resulting executable.

The header file also defines a class template connection. But it is also okay. Class templates can be defined multiple times in different translation units.

In fact, even regular class types can be defined multiple times (I've noticed that you've asked about this in the comments). Symbols created for member functions are usually weak symbols. Once again, weak symbols don't cause "multiple symbol definition" errors, because they can be redefined. Linker will just keep redefining weak symbols with names he has already seen until there will be just one symbol per member function left.

There are also other cases, where certain things (like inline functions and enumerations) can be defined several times in different translation units (see §3.2). And mechanisms of achieving this can be different (see class templates and inline functions). But the general rule is not to place stuff with global linkage in header files. As long as you follow this rule, you're really unlikely to stumble upon multiple symbol definitions problems.

And yes, include guards have nothing to do with this.

Upvotes: 0

Jerry Coffin
Jerry Coffin

Reputation: 490398

A header that causes linking problems when included in multiple translation units is one that will (attempt to) define some object (not just, for an obvious example, a type) in each source file where it's included.

For example, if you had something like: int f = 0; in a header, then each source file into which it was included would attempt to define f, and when you tried to link the object files together you'd get a complaint about multiple definitions of f.

The "technique" used in this header is simple: it doesn't attempt to define any actual objects. Rather, it includes some typedefs, and the definition of one fairly large class--but not any instances of that class or any instance of anything else either. That class includes a number of member functions, but they're all defined inside the function definition, which implicitly defines them as inline functions (so defining separately in each translation unit in which they're used is not only allowed, but required).

In short, the header only defines types, not objects, so there's nothing there to cause linker collisions when it's included in multiple source files that are linked together.

Upvotes: 2

orip
orip

Reputation: 75487

Templates are a special case in C++ regarding multiple definitions, as long as they're the same. See the "One Definition Rule" section of the C++ standard:

There can be more than one definition of a class type (Clause 9), enumeration type (7.2), inline function with external linkage (7.1.2), class template (Clause 14), non-static function template (14.5.6), static data member of a class template (14.5.1.3), member function of a class template (14.5.1.1), or template specialization for which some template parameters are not specified (14.7, 14.5.5) in a program provided that each definition appears in a different translation unit, and provided the definitions satisfy the following requirements. ....

This is then followed by a list of conditions that make sure the template definitions are identical across translation units.

This specific quote is from the 2014 working draft, section 3.2 ("One Definition Rule"), subsection 6.

Upvotes: 0

Paul Evans
Paul Evans

Reputation: 27577

Headers only include libraries like Boost C++ Libraries use (mostly) stand-alone templates and as so are compiled at compile-time and don't require any linkage to binary libraries (that would need separate compilation). One designed to never need linkage is the great Catch

Upvotes: 0

Anon Mail
Anon Mail

Reputation: 4770

If the header defines items, as opposed to just declaring them, then it's possible to include it in more than one translation unit (i.e. cpp file) and have multiple definitions and hence linker errors.

I've used boost's unit test framework which is header only. I include a specified header in only one of my own cpp files to get my project to compile. But I include other unit test headers in other cpp files which presumably use the items that are defined in the specified header.

Upvotes: 0

Related Questions