3

I have used include guards many times before, but never really understood how or why they work.

Why doesn't the following work?

#ifndef CAMERA_CLASS_HPP
#define CAMERA_CLASS_HPP


class camera_class
{
....
};

camera_class glcam = camera_class();


#endif // CAMERA_CLASS_HPP

The error is this: (You can probably guess what it's going to be from the title of this question!)

-------------- Build: Debug in System ---------------

Linking console executable: bin/Debug/System
/usr/bin/ld: error: obj/Debug/main.o: multiple definition of 'glcam'
/usr/bin/ld: obj/Debug/camera_class.o: previous definition here
/usr/bin/ld: error: obj/Debug/main.glfunc.o: multiple definition of 'glcam'
/usr/bin/ld: obj/Debug/camera_class.o: previous definition here
collect2: ld returned 1 exit status
Process terminated with status 1 (0 minutes, 0 seconds)
0 errors, 0 warnings

Also, could someone please explain to me why a header guard works?

FreelanceConsultant
  • 13,167
  • 27
  • 115
  • 225
  • 1
    Your understanding of what include guards do is mistaken. They prevent a header file being included twice *by the same source file*. They do not, and cannot, prevent a header being included twice by different source files. – john Sep 07 '12 at 14:53
  • Thanks John, I didn't understand that fundamental difference! – FreelanceConsultant Sep 07 '12 at 15:04

6 Answers6

8

The header guard will prevent multiple inclusions in a single translation unit. The header can (and is) being included in multiple translation units:

// a.cpp:
#include "camera.hpp"

// b.cpp
#include "camera.hpp"

This will produce a a.obj and a b.obj, each containing a definition for glcam. When linked together to produce the final binary you get the multiple definition error.

You need to declare glcam in the header and define it exactly once in a .cpp file:

// camera.hpp
...

extern camera_class glcam;

// camera.cpp
#include "camera.hpp"

camera_class glcam;
hmjd
  • 120,187
  • 20
  • 207
  • 252
  • This is the solution I chose. I don't like having an object of the `camera_class` in a completely different file (`main.opengl.cpp`) but it will have to do! – FreelanceConsultant Sep 07 '12 at 15:00
4

Root Cause:
The header guard prevents inclusion of the same header multiple times in the same translation unit but not across different translation units. When you include the same header file in multiple translation units then,
A copy of glcam is indeed being created in every translation unit where you include the header.
C++ standard mandates that each symbol can be defined only once(One Definition Rule) and hence the linker issues you the error.

Solution:
Do not create glcam in the header file. Instead it should be created in such a way that it gets defined only once. The correct way to do this is by using the keyword extern.

Community
  • 1
  • 1
Alok Save
  • 202,538
  • 53
  • 430
  • 533
  • Where should I create `glcam`? Should I create it in the .cpp file? – FreelanceConsultant Sep 07 '12 at 14:48
  • @EdwardBird: Updated to answer your query. – Alok Save Sep 07 '12 at 14:50
  • `extern` works, but I always have found that to be a little confusing. I prefer to use the method provided in my answer, which achieves basically the same result, but I find to be clearer. Obviously though, this is only a matter of taste. – Chad Sep 07 '12 at 14:54
  • What does `extern` do? Is it just a bodge to get it working? - I read the article, but not sure I understand it. – FreelanceConsultant Sep 07 '12 at 15:01
  • Ah I see, so it's like a linker 'switch' I guess you could say? Which just makes it behave differently. I think I'm going to have a look on Wikipedia about how C++ compilers work and what times what things are done. This should help me understand it better. :) – FreelanceConsultant Sep 07 '12 at 15:13
  • @EdwardBird: Yes. Modified previous comment to explain.If you create a object in file scope then by default it will have *external linkage*.Which means you can access the object in all other Translation Units. extern tells the compiler & linker that the object is defined in some other TU and allows you to create the object in one file but access it in other files. – Alok Save Sep 07 '12 at 15:19
3

It looks like you want to create a single glcam object that can be used in multiple places. I would do that by exposing a free function to return a static instance. This is similar to using extern, but I find it to be a little more explicit in its intent.

#ifndef CAMERA_CLASS_HPP
#define CAMERA_CLASS_HPP


class camera_class
{
....
};

camera_class& get_camera();


#endif // CAMERA_CLASS_HPP

// in the CPP

camera_class& get_camera()
{
   static camera_class the_camera;
   return the_camera;
}

This gives you the ability to use a single camera_class instance without relying on extern, but at the same time doesn't force you to use it as a singleton, as other areas of the code are free to create their own private instances as well.

This could be implemented as it is (a free-function) or as a static member function of the camera_class. I chose the former, based on some excellent advice from Scott Meyers:

If you're writing a function that can be implemented as either a member or as a non-friend non-member, you should prefer to implement it as a non-member function.

Source: http://www.drdobbs.com/cpp/how-non-member-functions-improve-encapsu/184401197

Chad
  • 18,706
  • 4
  • 46
  • 63
  • ... Although another question. Why is `camera_class& get_camera();` not a method inside the class? Why is it outside of the class definition? – FreelanceConsultant Sep 07 '12 at 15:09
  • Great question, and worthy of an answer in a more visible place than in a comment. See the last paragraph of my answer. That link has some excellent information that is specifically on topic for your original question. – Chad Sep 07 '12 at 15:11
2

Since you are including this file from several files, you are breaking the One Definition Rule:

In the entire program, an object or non-inline function cannot have more than one definition

You should put the glcam definition in a source file, rather than in a header file, or instead declare it as extern, and provide a definition in some source file.

mfontanini
  • 21,410
  • 4
  • 65
  • 73
1

The include guard prevents multiple instances of the text in your header from appearing in one compilation unit (ie a single .cpp you're building which gets built into a .o)

It doesn't prevent multiple instances of that text from appearing in multiple compilation units.

So at link time, each compilation unit that includes this header has a

 camera_class glcam = camera_class();

As a symbol. C++ can't decide when referring to "glcam" which single global definition you mean. The one from main.o or the one from camera_class.o?

Doug T.
  • 64,223
  • 27
  • 138
  • 202
0

It's working just fine, you're only getting one definition in each of your source files.

The problem is that you have multiple source files and the linker is finding multiple definitions.

In the header file you should put:

extern camera_class glcam;

And then in one and only one source file put what you used to have in the header:

camera_class glcam = camera_class();

At this point you'll need to be aware of initialization order problems. Don't try to use glcam from any static objects.

Mark Ransom
  • 299,747
  • 42
  • 398
  • 622