0

I'm currently writing a simple game with a 2D library and as C++ is a new language to me and as Java is my first fluent programming language, perhaps some bad habits are flowing through to this language that I don't fully understand. I have never had problems doing this in Java but in C++ it causes a ton of errors. As I don't want everything crammed into one class/header file, I've decided to split them up into packages and different classes, but I can't seem to do this without includng the same header files in different places. Here's an example.

Project.h

#ifndef PROJECT_H
#define PROJECT_H


#include "UIManager.h"//this is causing the error, when this, and the class instance is removed the program compiles and runs fine without error

using namespace gamelib;

class Project : public Game {


public:

    Project(int argc, char* argv[]);

    ~Project();

    void Update(int elapsed);
    void Draw(int elapsed);
    void Load();

    /*Background*/

    Texture * background; 
    Rectangle* backgroundRectangle; 

    UIManager ui;

};
#endif

UIManager.cpp

#include "UIManager.h"


void UIManager::load() {
    startMenuBackground = new Texture();
    startMenuBackground->Load("Sprites/Interface/Transparency.png", false);
    startMenuRectangle = new Rectangle(0.0f, 0.0f, Graphics::GetWidth() / 2, Graphics::GetHeight() / 2);
}

UIManager.h

#ifndef UIMANAGER_H
#define UIMANAGER_H

#include "Project.h"

class UIManager {

public:
    void load();

private:
    Texture * startMenuBackground;
    Rectangle * startMenuRectangle;

};
#endif

Now I need all of these includes so I can store the necessary textures, and the Texture and Rectangle come from the API that is being used as a namespace in Project.h

I also need a class instance in Project.h, UIManager ui; So i can use this in the Project.cpp file to call methods

How can I get around this without getting all these errors?

kbz
  • 984
  • 2
  • 12
  • 30
  • 1
    Just a general design comment, generally speaking you should avoid `using namespace` in header files. It ends up pretty much completely defeating the purpose of namespaces. – shuttle87 Nov 28 '14 at 23:27
  • As shown in the question, neither header is self-contained, and both depend on other declarations in other headers. This is usually a sign of trouble. The immediate problems are that `Texture` and `Rectangle` are not forward-declared (at minimum) and `Game` is not defined. This means we cannot test your code for you. Also, as I understand it, `using namespace gamelib;` does not place `class Project` into the `gamelib` namespace — it is not clear whether that was what you intended (but with the code shown, the `using namespace` declaration has no effect because nothing is in the namespace). – Jonathan Leffler Nov 29 '14 at 00:10

4 Answers4

1

If I understand your problem correctly, you want to avoid including header files multiple times.
In that case, what you should do is using Include guards. Which makes sure that the header is included once. Optionally you can use #pragma once at the top of your file but that's another discussion.

Example:

#ifndef UIMANAGER_H // you may choose another name than "UIMANAGER_H"
#define UIMANAGER_H

// Your header file code and includes here.
// Do this for your header files.

#endif

Now, do the same for the other header file but instead naming the macro PROJECT_H or similar.

Andreas DM
  • 10,685
  • 6
  • 35
  • 62
  • Thanks and yes, I guess that is the case, so I can use multiple occurrences of header files in different classes. Where would I put this code? I have tried adding this to every header file, and I have tried using #pragma once instead and it's still giving me this same error? – kbz Nov 28 '14 at 23:36
  • @Kieran after the `#define UIMANAGER_H` you should still add the `#include`s you need, (i.e `#include "Project.h"`). And Project.h should `#include "UIManager.h"`. – Andreas DM Nov 28 '14 at 23:40
0

A good practice is to add in the beginning and in the end of the .h files:

#ifndef FILENAME_H
#define FILENAME_H


//Your code here

#endif

Where FILENAME_H is unique for each .h file.

Those are compiler predirectives, which are not included in the executable, but changes the way that the compiler acts.

Adding those, the compiler won't add the same file twice for the same file. If it's already loaded, it won't load it.

Take in account than in C++, header files are parsed independently on each file of the project.

Desaroll
  • 323
  • 1
  • 9
  • Thanks I've just added that to every header file and it's still throwing the same errors. When I add the #include "UIManager.h" into Project.h, and add the class instance it throws errors. When I remove these, it's fine and runs as it should – kbz Nov 28 '14 at 23:37
  • If I'm not misguided, do you have the texture and rectangle classes definitions in the Project.h file? Or, where do you have them? – Desaroll Nov 28 '14 at 23:44
  • Yes they're defined in the namespace gamelib and thats why I need to include Project.h so I can use that namespace – kbz Nov 28 '14 at 23:45
  • Then, put the "using namespace gamelib" before the "#include "UIManager.h" and it should work fine. I would suggest using the whole definition (gamelib::Texture and gamelib::Rectangle) to prevent that issue. Of course, only in .h files. EDIT: (Explanation) The problem I saw there is that you are not using gamelib as namespace when you include "UIManager.h" and hence, Rectangle and Texture are not defined. – Desaroll Nov 28 '14 at 23:48
  • Oh that worked, I had to remove the #include "Project.h" and replace it with the namespace gamelib. I thought I could just inherit the namespace by including the Project.h – kbz Nov 28 '14 at 23:51
  • You were including the file before switching namespaces, and that created the error. Texture and Rectangle weren't accessible at the time when you included the file, sort to speak. – Desaroll Nov 28 '14 at 23:53
0

Typically, the fix would be to change your code to this...

#ifndef UIManager_H
#define UIManager_H

// It's pretty normal to put each class in its own header file.
#include "Texture.h"
#include "Rectangle.h"

class UIManager {

public:

    void load();

private:
    Texture * startMenuBackground;
    Rectangle * startMenuRectangle;

};
#endif // UIManager_H

Since you never instantiate either object though, you don't strictly need a full header.

#ifndef UIManager_H
#define UIManager_H

// Since we only use pointers to the classes, we can forward declare the classes
class Texture;
class Rectangle;

class UIManager {

public:

    void load();

private:
    Texture * startMenuBackground;
    Rectangle * startMenuRectangle;

};

#endif // UIManager_H
QuestionC
  • 10,006
  • 4
  • 26
  • 44
  • The Texture and Rectangle classes are in the namespace gamelib though – kbz Nov 28 '14 at 23:48
  • You can stick the forward declarations in a namespace then. Like.... `namespace gamelib { class Texture; class Rectangle; }` (but with newlines). That also doesn't preclude the first solution. You can use the same namespace across two different headers. – QuestionC Nov 29 '14 at 03:00
-1

You can use the macros #ifndef and #define. This is a common pattern. For example, in your UIManager.h file, have the following:

#ifndef __UI_MANAGER_H__
#define __UI_MANAGER_H__

#include "Project.h"
//Rest of UIManager class definition


#endif

In you Project.h file, have the following:

#ifndef __PROJECT_H__
#define __PROJECT_H__

#include "UIManager.h"
//Rest of Project class definition


#endif

This allows the compiler to compile without errors when it sees circular includes. But it's more preferable to use class forward declaration if possible, but this is a completely different topic.

The macro names __UI_MANAGER_H__ and __PROJECT_H__ are purely randomly chosen. You can choose to use a different naming convention.

Community
  • 1
  • 1
Kevin Le - Khnle
  • 10,579
  • 11
  • 54
  • 80
  • Not my down-vote, and the basic mechanism is valid, but using double-underscore in C++ is not allowed in user code; it is reserved for the implementation. So, the system headers can use names with double-underscore, but your headers cannot. – Jonathan Leffler Nov 29 '14 at 00:00