Ridding myself of circular dependency

I currently have two singletons that are unfortunately depending on one another.
I'm following SDL Game Development by Shaun Mitchell, so please don't choke me out for using singletons, I'm just following the book. I want to clean up my includes, but this dependency is giving me problems.

I have one singleton called Game, typedef'd as TheGame, that has a bool that holds it's running state when this bool goes false it ends the game loop in main. I also have an InputHandler Singleton, typedef'd as TheInputHandler that has an event handler that changes the bool from TheGame when the SDL_Quit event is seen. The problem is that Game has to call TheInputHandler to initialize the controls. How do I break this circular dependency?

As I said I know my includes are a mess, but here's the relevant parts of the code. I set the really important bits to bold.

For the record, all of my code does compile and it runs as I expect it to. I'm just leary of having co-dependent classes.

main.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include "Game.h"

int main(int argc, char** argv)
{
    Uint32 frameStart, frameTime;
    const int FPS = 60;
    const int DELAY_TIME = 1000.0f / FPS;
    const int WIDTH = 800;
    const int HEIGHT = 600;


    if(TheGame::Instance()->init("SDL Test", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, WIDTH, HEIGHT, SDL_WINDOW_SHOWN))
    {
        while(TheGame::Instance()->getRunning())
        {
            frameStart = SDL_GetTicks();

            TheGame::Instance()->handleEvents();
            TheGame::Instance()->update();
            TheGame::Instance()->render();

            frameTime = SDL_GetTicks() - frameStart;
            if(frameTime < DELAY_TIME)
            {
                SDL_Delay((int)(DELAY_TIME - frameTime));
            }
        }
    }
    TheGame::Instance()->clean();

    return 0;
}


Game.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#ifndef GAME_H
#define GAME_H

#include <iostream>
#include "C:\CPP\SDL2-2.0.4\i686-w64-mingw32\include\SDL2\SDL.h"
#include "C:\CPP\SDL2_image-2.0.1\i686-w64-mingw32\include\SDL2\SDL_image.h"
#include "InputHandler.h"
#include "GameStateMachine.h"
#include "MenuState.h"
#include "PlayState.h"


class Game
{
    public:
        static Game* Instance()
        {
            if(s_pInstance == 0)
            {
                s_pInstance = new Game();
                return s_pInstance;
            }
            return s_pInstance;
        }

        //Member Functions
        SDL_Renderer* getRenderer() const {return m_pRenderer;}
        bool init(const char* title, int xPos, int yPos, int width, int height, int flags);
        void render();
        void handleEvents();
        void update();
        void clean();

        bool getRunning(){return m_bRunning;}
        void setRunning(bool choice) {m_bRunning = choice;}

    private:
        Game();
        static Game* s_pInstance;

        GameStateMachine* m_pGameStateMachine;

        bool m_bRunning = true;

        //GameStateMachine* m_pGameStateMachine;

        SDL_Window* m_pWindow;
        SDL_Renderer* m_pRenderer;
        SDL_Texture* m_pTexture;
};
typedef Game TheGame;
#endif // GAME_H 


Game.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include "Game.h"

Game* Game::s_pInstance = 0;

Game::Game()
{

}

bool Game::init(const char* title, int xPos, int yPos, int width, int height, int flags)
{
    bool success = true;

    if(SDL_Init(SDL_INIT_EVERYTHING) < 0)
    {
        std::cout << "Could not initialize SDL. SDL_Error: " << SDL_GetError() << std::endl;
        success = false;
    }
    else
    {
        m_pWindow = SDL_CreateWindow(title, xPos, yPos, width, height, flags);
        if(m_pWindow == NULL)
        {
            std::cout << "Could not create window. SDL_Error: " << SDL_GetError() << std::endl;
            success = false;
        }
        else
        {
            m_pRenderer = SDL_CreateRenderer(m_pWindow, -1, SDL_RENDERER_ACCELERATED);
            if(m_pRenderer == NULL)
            {
                std::cout << "Could not create renderer. SDL_Error: " << SDL_GetError() << std::endl;
                success = false;
            }
        }
    }

    m_pGameStateMachine = new GameStateMachine();
    m_pGameStateMachine->changeState(new MenuState());

    TheInputHandler::Instance()->initializeJoysticks();

    return success;
}



InputHandler.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#ifndef INPUTHANDLER_H
#define INPUTHANDLER_H

#include <vector>
#include <iostream>
#include "C:\CPP\SDL2-2.0.4\i686-w64-mingw32\include\SDL2\SDL.h"
#include "Vector2D.h"
#include "Game.h"

enum mouse_buttons{ LEFT = 0, MIDDLE = 1, RIGHT = 3 };

class InputHandler
{
    public:
        static InputHandler* Instance()
        {
            if(s_pInstance == 0)
            {
                s_pInstance = new InputHandler();
            }
            return s_pInstance;
        }

        void update();
        void clean();

        //joystick functions
        void initializeJoysticks();
        bool joysticksInitialized(){ return m_bJoysticksInitialized; }
        int xvalue(int joy, int stick);
        int yvalue(int joy, int stick);
        const int m_joystickDeadZone = 10000;

        //button functions
        bool getButtonState(int joy, int buttonNumber) { return m_buttonStates[joy][buttonNumber]; }

        //mouse functions
        Vector2D* getMousePosition(){ return m_mousePosition; }
        bool getMouseButtonState(int buttonNumber) { return m_mouseButtonStates[buttonNumber]; }


        //keyboard functions
        bool isKeyDown(SDL_Scancode key);
        const Uint8* m_keystates;

    protected:

    private:
        InputHandler();
        ~InputHandler(){}

        // Joystick event handler functions
        void onJoystickAxisMove(SDL_Event &event);
        void onJoystickButtonDown(SDL_Event &event);
        void onJoystickButtonUp(SDL_Event &event);

        //joystick variables
        std::vector<SDL_Joystick*> m_joysticks;
        std::vector<std::pair<Vector2D*, Vector2D*>> m_joystickValues;
        bool m_bJoysticksInitialized;
        std::vector<std::vector<bool>> m_buttonStates;
        static InputHandler* s_pInstance;

        // Mouse event handler functions
        void onMouseMove(SDL_Event &event);
        void onMouseButtonDown(SDL_Event &event);
        void onMouseButtonUp(SDL_Event &event);

        //mouse variables
        std::vector<bool> m_mouseButtonStates;
        Vector2D* m_mousePosition;

        void onKeyDown();
        void onKeyUp();

};
typedef InputHandler TheInputHandler;

#endif // INPUTHANDLER_H 


InputHandler.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include "InputHandler.h"

void InputHandler::update()
{
    SDL_Event event;
    while(SDL_PollEvent(&event))
    {
        switch(event.type)
        {
            case SDL_QUIT:
                TheGame::Instance()->setRunning(false);
                break;
            case SDL_JOYAXISMOTION:
                onJoystickAxisMove(event);
                break;
            case SDL_JOYBUTTONDOWN:
                onJoystickButtonDown(event);
                break;
            case SDL_JOYBUTTONUP:
                onJoystickButtonUp(event);
                break;
            case SDL_MOUSEMOTION:
                onMouseMove(event);
                break;
            case SDL_MOUSEBUTTONDOWN:
                onMouseButtonDown(event);
                break;
            case SDL_MOUSEBUTTONUP:
                onMouseButtonUp(event);
                break;
            case SDL_KEYDOWN:
                onKeyDown();
                break;
            case SDL_KEYUP:
                onKeyUp();
                break;
            default:
                break;
        }
    }
}
Last edited on
I don't see the problem. Having two classes that call each other's functions is perfectly normal.
Well the problem is an architectural one.

In my opinion, both "TheGame" and (at least) "TheInputHandler" shouldn't exist -- it seems like an artifact of trying to shoehorn object-orientation into the problem.

Singletons are often named antipatterns. That's because they abuse the nature of classes --- classes are intended to represent distinct variants of the same kind of object --- not global state.

The only purpose of (the only) InputHandler object is as a container for names dealing with input and reacting to them. Use a namespace instead, and move the information about how you react to input away.

If there were simply functions in place of the class garbage, we might more easily realize that getting input has nothing to do with what the game does with that input -- and the code that gets the input shouldn't care at all about the structure of the game at all -- those concerns are totally unrelated.
I understand that singletons are not generally liked for the reasons you're stating. I'm still learning how to structure larger programs and I currently don't have the knowlege or experience to argue against or deviate from the author of this book's design. Where do you gain knovlege about things like this besides from reading someone elses work? I find so many programming books with poor examples and designs. How do you learn the proper way with so many bad examples out there?
Last edited on
Topic archived. No new replies allowed.