circular include

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • Jordi
    New Member
    • Feb 2007
    • 9

    #1

    circular include

    I'm having a problem which I think has to do with the circular use of inlcuding header files.
    The short version of my code would look a bit like this, I think:
    GameEngine.h:
    Code:
    #ifndef GAME_ENGINE
    #define GAME_ENGINE
    
    #include "Player.h"
    
    class GameEngine
    {
    public:
        GameEngine ();
    // ..
    
    private:
        // ..
        Player* players[2];
    };
    
    #endif
    Player.h:
    Code:
    #ifndef PLAYER
    #define PLAYER
    
    #include "GameEngine.h"
    
    // This class contains pure virtual functions!
    class Player
    {
    public:
        // ..
    protected: 
    //..
    private:
        GameEngine* game; // line 37
    // ..
    };
    
    #endif
    These are the errors I'm getting:
    Player.h:37: error: ISO C++ forbids declaration of `GameEngine' with no type
    Player.h:37: error: expected `;' before '*' token

    If I change line 37 into GameEngine Game; (so it's not a pointer anymore), the error changes to:
    Player.h:37: error: `GameEngine' does not name a type

    If I remove line 37, a lot more errors are found:
    Players\RandomP layer.cpp:12: error: expected class-name before '{' token
    Players\RandomP layer.cpp:19: error: expected `,' or `...' before "note"
    Players\RandomP layer.cpp:19: error: ISO C++ forbids declaration of `Notification' with no type
    Players\RandomP layer.cpp:28: error: class `RandomPlayer' does not have any field named `Player'
    Players\RandomP layer.cpp:34: error: `name' undeclared (first use this function)
    Players\RandomP layer.cpp:34: error: (Each undeclared identifier is reported only once for each function it appears in.)
    GameEngine.h:19 : error: `Player' has not been declared
    GameEngine.h:19 : error: ISO C++ forbids declaration of `parameter' with no type
    GameEngine.h:30 : error: ISO C++ forbids declaration of `Player' with no type
    GameEngine.h:30 : error: expected `;' before '*' token
    Player.cpp:38: warning: unused variable 'move'
    :: === Build finished: 10 errors, 1 warnings ===

    All of these errors seem to have to do with types like Player and GameEngine not being known. Does anybody have any idea how I can repair this?

    Complete header files: (there are of course more files, so if it is necessary to see those to, just ask for them)

    GameEngine.h:
    Code:
    #ifndef GAME_ENGINE
    #define GAME_ENGINE
    
    #include <vector>
    
    #include "Board.h"
    #include "Player.h"
    #include "Players.h"
    
    class GameEngine
    {
    public:
        GameEngine ();
        GameEngine (std::vector<CellOwner>);
    
        Board* getBoard () const;
        int getTurns () const;
        bool end () const;
        bool setPlayer (const CellOwner, Player*);
        bool removePlayer (const CellOwner);
        void swapPlayers ();
        void restart ();
        void next ();
        bool undo ();
        bool redo ();
    
    private:
        unsigned int turn;
        Board* board;
        Player* players[2];
    
        std::vector<unsigned int> moves;
    };
    
    #endif
    Player.h:
    Code:
    #ifndef PLAYER
    #define PLAYER
    
    #include <string>
    #include <list>
    
    #include "GameEngine.h"
    #include "Board.h"
    
    
    enum Notification { CLEAR };
    
    class Player
    {
    public:
        virtual ~Player();
    
        void addObserver (Player* observer);
        void removeObserver (Player* observer);
        void makeMove (const Board*);
        std::string getName () const;
        bool canLearn () const;
        bool isHuman () const;
    
        virtual void setName (const std::string name) = 0;
        virtual void learn (const Board*, const int) = 0;
        virtual void notify (const Notification) = 0;
        virtual int thinkMove (const Board*) = 0;
        virtual void giveTurn () = 0;
    protected: // Protected, because observers can be asked for help
        std::list<Player*> observers;
        std::string name;
        bool turn;
    
        Player (const bool, const bool);
    private:
        GameEngine* game;
        const bool human;
        const bool teachable;
    };
    
    #endif
    Thanks for any help!
  • Banfa
    Recognized Expert Expert
    • Feb 2006
    • 9067

    #2
    Originally posted by Jordi
    I'm having a problem which I think has to do with the circular use of inlcuding header files.
    Correct!

    The problem is GameEngine.h includes Player.h but has (correctly) set an double include protection symbol so when Player.h tries to include GameEngine.h it gets an empty file because of this double include protection. Then class Player tries to use class GameEngine, however because the copy of GameEngine.h that Player.h included was empty the class GameEngine has not been defined and you get all the errors.

    However all is not lost. If you look at class GameEngine you will see it only uses class Player in the context of a pointer to Player (i.e. doesn't try to access any members) and in fact class Player only uses class gameEngine in the context of a pointer to GameEngine. In this case the compiler only needs to reserve space for a pointer so it only needs to know that the class exists not what it's details are.

    There is a way to inform a the compiler that a class exists (or will exist) but define any of it's details, it is called "forward declaring the class (or structure)" and the syntax is

    Code:
    class GameEngine;
    You can remove the inclusion of the headers and forward declare your classes instead, in GameEngine.h this looks like

    Code:
    #ifndef GAME_ENGINE
    #define GAME_ENGINE
    
    class Player.h;
    
    class GameEngine
    {
    public:
        GameEngine ();
    // ..
    
    private:
        // ..
        Player* players[2];
    };
    
    #endif

    Comment

    • Jordi
      New Member
      • Feb 2007
      • 9

      #3
      Thank you very much for you quick reply! I had figured parts of what you said out, but your explanation was very good and I understand a lot better now.

      I'm assuming though that you meant to write "class Player;" instead of "class Player.h;".

      This deals with my problem very well, but now I have another (far less important question):
      From your explanation, I gather that I can always use forward declaration in header files where I'm only using a pointer to an object. I would still need to include the header files in the *.cpp files (because I'll be using methods of the forward declared class), so I might as well do it in a header file. However, it isn't strictly necessary there since a forward declaration would do. So, what would be considered good practice?

      Comment

      • Banfa
        Recognized Expert Expert
        • Feb 2006
        • 9067

        #4
        Originally posted by Jordi
        I'm assuming though that you meant to write "class Player;" instead of "class Player.h;".
        Err, yes I did.

        Originally posted by Jordi
        From your explanation, I gather that I can always use forward declaration in header files where I'm only using a pointer to an object. I would still need to include the header files in the *.cpp files (because I'll be using methods of the forward declared class), so I might as well do it in a header file. However, it isn't strictly necessary there since a forward declaration would do. So, what would be considered good practice?
        This sort of thing is a lot down to personal choice.

        For instance in some companies #including from header files at all is frowned upon and you tend to have long lists of headers in you c/cpp files. Personally I don't agree with this because those long lists of headers just get very unweidly and you often end up with unrequired headers in files.

        Another not uncommon approach is to include all headers into another single header file in the correct order and then include that header into your c/cpp files. If you use precompiled headers this doesn't have too much effect of compile time. I don't like this approach because it is a bit of a sledge hammer approach.

        So I say if a header requires another header then include it, however try not to let the header inclusion go too deep, not more than 1 or 2 levels.

        As to forward declaration it is required to get round the problem you have had but I don't think it is very elegant so my opinion is that you should arrange your header inclusion to reduce the amount of required forward declaration to a minimum.

        However note this is all opinion, I have not seen many "best practices" covering the topic of forward declaration.

        Comment

        • Jordi
          New Member
          • Feb 2007
          • 9

          #5
          Thanks again for your reply!

          The solution worked for a while. I forward declared GameEngine in Player.h and then included it in Player.cpp, where I could use all of GameEngine's public methods. However, I now derived a class from Player and put it in a file called RandomPlayer.cp p. I've created no header file for this, because this class is really basic and the public interface is almost exactly the same as Player's. However, Including both Player.h and GameEngine.h in RandomPlayer.cp p gives me this errors:
          E:\Jordi\Develo pment\Projects\ FourFun\lib\Pla yers\RandomPlay er.cpp:38: error: invalid use of undefined type `struct GameEngine'
          E:\Jordi\Develo pment\Projects\ FourFun\lib\Pla yers\..\Player. h:10: error: forward declaration of `struct GameEngine'
          :: === Build finished: 2 errors, 0 warnings ===

          I have also forward declared Player in GameEngine.h, so those files are not directly linking to eachother anymore.

          Does anyone know why this is and what I can do about it?

          Comment

          • Jordi
            New Member
            • Feb 2007
            • 9

            #6
            Never mind, the new problem was similar to the old one, I just didn't see it.

            Comment

            • Banfa
              Recognized Expert Expert
              • Feb 2006
              • 9067

              #7
              Originally posted by Jordi
              Never mind, the new problem was similar to the old one, I just didn't see it.
              Hooray, because while I was aware of the second question I had kind of lost the thread (or was it is plot???).

              Happy to know your current problems are solved.

              Comment

              Working...