1

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.

Community
  • 1
  • 1
johnbakers
  • 24,158
  • 24
  • 130
  • 258
  • 4
    The very first thing in that header file is `#ifndef WEBSOCKETPP_CONNECTION_HPP` `#define WEBSOCKETPP_CONNECTION_HPP`. And the closing `#endif` is the last thing in the file. So I don't understand your question. – 5gon12eder Nov 02 '15 at 21:58
  • 2
    @5gon12eder: That only protects against including the header more than once *in a single translation unit*. He's talking about including it in multiple translation units. – Jerry Coffin Nov 02 '15 at 22:02
  • 2
    You should specify what you don't understand in that headerfile. We can't guess that. – Johannes Schaub - litb Nov 02 '15 at 22:02
  • 1
    @5gon12eder The linked SO question specifically is about the #ifndef NOT preventing multiple definition errors, which is why I'm asking this question. – johnbakers Nov 02 '15 at 22:05
  • The problem is that linking is a separate step. The linker can tell you that the same symbol appears more than once but the compiler doesn't know that because the compiler complies each translation unit separately. So there's no way to "protect" against this - if you get link errors, you need to work out why and fix it. – Jonathan Potter Nov 02 '15 at 22:09
  • @hellofunk Okay, I misunderstood that. But I still don't know what you are asking. The header does not contain anything that will violate the one-definition-rule even when `#include`d in multiple translation units that are then linked together. – 5gon12eder Nov 02 '15 at 22:09
  • @5gon12eder cool, so I guess my question is, how is that possible? Because everything in the header would indeed be defined more than once, due to each TU including it, no? I appreciate you confirming that it works, but this question is about why it works. – johnbakers Nov 02 '15 at 22:11
  • Have a look at Jerry Coffin's answer. I think he nailed it. – 5gon12eder Nov 02 '15 at 22:13

5 Answers5

2

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.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • Interesting, I thought that a class definition was indeed a "definition" in the sense of the "one definition rule" but it sounds like the ODR is referring to something more specific. A class by itself is not an object, yet there is a distinction between a declaration of a class and a definition of a class; if you define a class in a header, this is not affected by ODR until you actually instantiate it? – johnbakers Nov 02 '15 at 22:13
0

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.

Anon Mail
  • 4,660
  • 1
  • 18
  • 21
  • I don't follow... the question is whether or not this library is designed in a way to prevent multiple definition errors. I gather you are saying that it is not? – johnbakers Nov 02 '15 at 22:08
  • I'm saying that the boost unit test framework is not designed to prevent multiple definitions. As others have said, it looks like this library is designed to prevent this. – Anon Mail Nov 02 '15 at 22:10
0

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

Paul Evans
  • 27,315
  • 3
  • 37
  • 54
0

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.

orip
  • 73,323
  • 21
  • 116
  • 148
  • Ok, you say that "templates are a special case" but that quote seems to suggest that even non-templated class types can also have more than one definition, as long as they are the same, is that right? – johnbakers Nov 02 '15 at 22:20
0

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.

Nikita Kakuev
  • 1,096
  • 9
  • 13