Search code examples
c++singletoncircular-dependency

Resolve circular dependancy when using a Singleton in C++


So I have a problem when using a singleton Game class and the Story class.

Game.hpp

#ifndef GAME_HPP
#define GAME_HPP

#include <memory>

class Phase; // Forward declaration

class Game
{
  private:
    static inline Game* instance_reference_{nullptr};
    std::unique_ptr<Phase> current_phase_;
    bool is_running_{true};

    explicit Game() {};
public:
    virtual ~Game();
    const static Game* getInstance();

and of course the getInstance() method is defined in a .cpp file.

Story.hpp

#ifndef PHASE_HPP
#define PHASE_HPP

class Game; // Forward declaration

class Phase
{
  protected:
    const Game* game_;
  public:
    explicit Phase() : game_{ Game::getInstance() } {};
    virtual ~Phase() = default;
    virtual void handlePhase() = 0;
};
#endif

And some other classes inherit from the Phase class. Also, currently there is no Phase.cpp file.

Right now I cannot compile because the Game::getInstance() is nowhere declared. but when I swap to #include "Game.hpp"instead of the forward declaration, when I compile I get this error:

/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/13.2.1/../../../../include/c++/13.2.1/bits/unique_ptr.h:97:16: error: invalid application of 'sizeof' to an incomplete type 'Phase'
   97 |         static_assert(sizeof(_Tp)>0,
      |                       ^~~~~~~~~~~
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/13.2.1/../../../../include/c++/13.2.1/bits/unique_ptr.h:404:4: note: in instantiation of member function 'std::default_delete<Phase>::operator()' requested here
  404 |           get_deleter()(std::move(__ptr));
      |           ^
./Game.hpp:36:14: note: in instantiation of member function 'std::unique_ptr<Phase>::~unique_ptr' requested here
   36 |     explicit Game() {};
      |              ^
./Game.hpp:13:7: note: forward declaration of 'Phase'
   13 | class Phase; // Forward declaration
      |       ^

So all in all, when I implement it how it is usually done, I have problems calling Game::getInstance() in the Phase constructor, but when I try to resolve this, it still won't compile.


Solution

  • If you forward declare a class:

    class Game; // Forward declaration
    

    You can only use it in ways that don't require you to know anything about it. So in header files this basically means you can only declare pointers to it.

    That means this is not going to work:

    class Phase
    {
      protected:
        const Game* game_;
      public:
        /// This will fail
        /// You don't know that Game has any members.
        /// So you can't call functions at this point.
        explicit Phase() : game_{ Game::getInstance() } {};
        virtual ~Phase() = default;
        virtual void handlePhase() = 0;
    };
    

    The solution is to change the header to

    class Phase
    {
      protected:
        const Game* game_;
      public:
        explicit Phase();
        virtual ~Phase() = default;
        virtual void handlePhase() = 0;
    };
    

    Then in the Phase.cpp file add:

    #include "Phase.h"
    #include "Game.h"
    
    Phase::Phase() : game_{ Game::getInstance() } {};