I've taken a look at the Sourcemaking example and for me the implementation example really sucks; having to create new instances upon every state change:
https://sourcemaking.com/design_patterns/state/cpp/1
Personally as someone who's designed state machines in electronics with JK flip flops, I would use a similar but semantically different approach. The complexity in state machines involves the action performed according to the state and input; typically in C you would do this with lots of switch statements and possibly arrays describing how to handle the current state
and new input
aka event
.
So to me the OO approach to this would be to model the event handler
. This would have an interface which describes the format of the inputs. You then have different implementations of that interface for each different state. With that, the state machine can simply implement a collection of states to event handlers - array, vector or map. Although the handlers still may contain case statements, the overall spaghettiness is very much reduced. You can easily extend the design with new state handlers as and when necessary:
So you could have something like this:
#include <map>
typedef enum
{
//TODO : state list, e.g.
eOff,
eOn
}
teCurrentState;
typedef struct
{
//TODO : Add inputs here, e.g.
bool switch1;
}
tsInputDesc;
typedef struct
{
//TODO : Add outputs here, e.g.
bool relay1;
}
tsOutputDesc;
// ------------------------------------------------
class IEventHandler
{
public:
virtual ~IEventHandler() {}
// returns new state
virtual teCurrentState handleInput(tsInputDesc const& input, tsOutputDesc& output) = 0;
};
// ------------------------------------------------
class OnStateHandler : public IEventHandler
{
public:
virtual teCurrentState handleInput(tsInputDesc const& input, tsOutputDesc& output) override
{
//TODO : IMPLEMENT
teCurrentState newState = TODO....
return (newState);
}
};
// ------------------------------------------------
class OffStateHandler : public IEventHandler
{
public:
virtual teCurrentState handleInput(tsInputDesc const& input, tsOutputDesc& output) override
{
//TODO : IMPLEMENT
teCurrentState newState = TODO....
return (newState);
}
};
// ------------------------------------------------
class StateMachine
{
protected:
teCurrentState mCurrentState;
std::map<teCurrentState, IEventHandler*> mStateHandlers;
void makeHandlers()
{
mStateHandlers[eOff] = new OffStateHandler();
mStateHandlers[eOn] = new OnStateHandler();
}
public:
StateMachine()
{
makeHandlers();
mCurrentState = eOff;
}
void handleInput(tsInputDesc const& input, tsOutputDesc output)
{
teCurrentState newState = mStateHandlers[mCurrentState]->handleInput(input, output);
mCurrentState = newState;
}
};
// ------------------------------------------------
void runFsm()
{
StateMachine fsm;
tsInputDesc input;
tsOutputDesc output;
bool alive = true;
while (alive)
{
// TODO : set input according to....inputs (e.g. read I/O port etc)
fsm.handleInput(input, output);
// TODO : use output
}
}