Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

state pattern C++

I'm trying to instigate a simple State pattern, after following some of the excellent tutorials here: http://gameprogrammingpatterns.com/state.html

I am half way through this current tutorial, and I am trying to replicate the static instances of each state, by containing them within the base class. However, when it comes to switching states, g++ is throwing this error.

   state_test.cpp: In member function ‘virtual void Introduction::handleinput(Game&, int)’:
state_test.cpp:55:16: error: cannot convert ‘Playing*’ to ‘GameState*’ in assignment
    game.state_ = &GameState::play;
                ^

Now, I understand the error involves the conversion of the pointer, but I am really struggling to see how to fix it. As I was following this guys code, I kind of expected it to work, but because he is changing it as he goes along and trying to reinforce best practice, I don't have his complete source code to follow. However, I feel it is important for me to understand the code at this stage, before I move through the rest of the tutorial.

Following is the code I created, attempting to replicate his state system:

#include <iostream>

class Game;
class Introduction;
class Playing;

class GameState
{
public:

    static Introduction intro;
    static Playing play;

    virtual ~GameState() {std::cout << "an undefined GameState has been destroyed" << std::endl;}
    virtual void handleinput(Game& game, int arbitary) {}
    virtual void update(Game& game) {}

};

class Game
{
public:

    Game()
    {}
    ~Game()
    {}

    virtual void handleinput(int arbitary)
        {
            state_->handleinput(*this, arbitary);
        }

    virtual void update()
        {
            state_->update(*this);
        }

//private: 
    GameState* state_;
};

class Introduction : public GameState
{
public:

    Introduction()  
    {
        std::cout << "constructed Introduction state" << std::endl;
    }

    virtual void handleinput(Game& game, int arbitary) 
        {
            if (arbitary == 1)
            game.state_ = &GameState::play;
        }

    virtual void update(Game& game) {}
};

class Playing : public GameState
{
public:
    Playing()   {std::cout << "constructed Playing state" << std::endl;}

    virtual void handleinput(Game& game, int arbitary) 
        {
            if (arbitary == 0)
            game.state_ = &GameState::intro;
        }

    virtual void update(Game& game) {}
};

int main(int argc, char const *argv[])
{
    Game thisgame;

    return 0;
}

Any ideas why my implementation isn't compiling?

EDIT:

So in response to the earlier tutoring, for which I was very grateful, I revised the code. I start by putting it all in separate files, but this was more trouble than it was worth for such a small amount of test code. I simply rewrote one header file which declared the classes, and then defined them in the .cpp file.

Here is the .h file:

class Introduction;
class Playing;
class Game;
class GameState;

class GameState
{
    public:

    static Introduction intro;
    static Playing play;

    virtual ~GameState();
    virtual void handleinput(Game& game, int arbitary);
    virtual void update(Game& game);

};


class Introduction : public GameState
{
public:

    Introduction();

    virtual void handleinput(Game& game, int arbitary); 

    virtual void update(Game& game);

};

class Playing : public GameState
{
public:
    Playing();

    virtual void handleinput(Game& game, int arbitary);

    virtual void update(Game& game);    
};


class Game
{
public:

    Game();

    ~Game();

    virtual void handleinput(int arbitary);

    virtual void update();

    GameState* state_;

};

And here is the .cpp file:

#include <iostream>
#include "state.h"


GameState::~GameState() 
    {std::cout << "Exiting Game State Instance" << std::endl;}
void GameState::handleinput(Game& game, int arbitary) 
    {}
void GameState::update(Game& game) 
    {}



Game::Game()
    {}
Game::~Game()
    {}
void Game::handleinput(int arbitary)
        {
            state_->handleinput(*this, arbitary);
        }

void Game::update()
        {
            state_->update(*this);
        }


Introduction::Introduction()    
    {
        std::cout << "constructed Introduction state" << std::endl;
    }

void Introduction::handleinput(Game& game, int arbitary) 
        {
            if (arbitary == 1)
            game.state_ = &GameState::play;
        }

void Introduction::update(Game& game) {}


Playing::Playing()  
        {
            std::cout << "constructed Playing state" << std::endl;
        }

void Playing::handleinput(Game& game, int arbitary) 
        {
            if (arbitary == 0)
            game.state_ = &GameState::intro;
        }

void Playing::update(Game& game) {}



int main(int argc, char const *argv[])
{
    Game mygame;
    return 0;
}

And I still can't get it to work. The previous error has gone away, but I am struggling to access the static instances of "introduction" and playing inside of the base class. The error thrown is:

/tmp/ccH87ioX.o: In function `Introduction::handleinput(Game&, int)':
state_test.cpp:(.text+0x1a9): undefined reference to `GameState::play'
/tmp/ccH87ioX.o: In function `Playing::handleinput(Game&, int)':
state_test.cpp:(.text+0x23f): undefined reference to `GameState::intro'
collect2: error: ld returned 1 exit status

I thought i had it sussed! So frustrated!

I should add that the answer provided by RustyX does compile, however I have to move the instances of "playing" and "introduction" outside of the class definition, and I can then no longer set them to be static, I believe this is important because I only need one instance of each and I would like them to be initialised as early as possible.

like image 644
William Barnes Avatar asked Aug 29 '16 11:08

William Barnes


People also ask

What is State pattern in C#?

State is a behavioral design pattern that allows an object to change the behavior when its internal state changes. The pattern extracts state-related behaviors into separate state classes and forces the original object to delegate the work to an instance of these classes, instead of acting on its own.

What is State pattern example?

Example. The State pattern allows an object to change its behavior when its internal state changes. This pattern can be observed in a vending machine. Vending machines have states based on the inventory, amount of currency deposited, the ability to make change, the item selected, etc.

What is C pattern design?

Design Patterns in the object-oriented world is a reusable solution to common software design problems that occur repeatedly in real-world application development. It is a template or description of how to solve problems that can be used in many situations. "A pattern is a recurring solution to a problem in a context."

Does C have design patterns?

All functions in C always use the same basic pattern (syntax). This syntax can be captured in a "Design Pattern".


1 Answers

The problem is that the compiler reads the file from top to bottom. At the line that contains

game.state_ = &GameState::play;

he still doesn't know that Playing inherits from GameState. It only knows that Playing is a class that will be declared later.

You should split the class declarations from method implementations. Have all class declarations first and method implementations later. In bigger project you would split them all to individual *.h and *.cpp files and this ordering would happen naturally.

Shortened example:

class Playing : public GameState
{
public:
    Playing();

    virtual void handleinput(Game& game, int arbitary);

    virtual void update(Game& game);
};

// Declarations of other classes...


Playing::Playing() {
    std::cout << "constructed Playing state" << std::endl;
}

void Playing::handleinput(Game& game, int arbitrary) {
    if (arbitary == 0)
        game.state_ = &GameState::intro;
    }
}

void Playing::update(Game& game) {
}

You can leave some of the method inside the class declaration. Usually it's done if the method is small, would benefit from inlining and doesn't have this kind of circular dependency problem.

like image 118
michalsrb Avatar answered Nov 10 '22 01:11

michalsrb