tags:

views:

287

answers:

11

Dear All,

I'm new to C++.

How can I implement a State Machine in C++ ?

I'm getting only the messages and should know the next state.

What is the proper structure I need to use ?

Thanks, Igal

+10  A: 

For simple state machines you can just use a switch statement inside a loop, e.g.

for (;;)
{
    switch (state)
    {

    case STATE_1:
        // do stuff
        // maybe change state
        break;

    case STATE_2:
        // do stuff
        // maybe change state
        break;

    case STATE_3:
        // do stuff
        // maybe change state
        break;

    // ...

    }
}
Paul R
Only really for very simple state machines. And it's not OO at all. Your code is C-only, no C++ is used.
Peter K.
@Peter K.: he didn't ask for an OO solution and he didn't state that he needed to use language features which are specific to C++. Since the above code compiles in C++ (as well as C and Objective C) I consider that it meets the requirements as stated. And since this is homework I am assuming that only a very simple state machine is needed.
Paul R
@Paul: But it's ugly code! :-)
Peter K.
@Peter K.: Beauty is in the eye of the beholder. :-)
Paul R
@Paul: Indeed it is!
Peter K.
"should know the next state"... I don't see this in your code...
Lorenzo
Begs the question of whether simplicity is beauty.
John K
@Lorenzo: the next state is determined by the logic for the current state, i.e. the code for the case label which matches the current state.
Paul R
@Paul R:Thank you for answering. Your code is very interesting. I hope to write the whole solution and post it here (if it will run, of course ;-).
Igal Spector
@Igal: you're welcome - and good luck with your project
Paul R
+3  A: 

Unless you are implementing a state machine for the sake of implementing one, I would highly recommend that you use a state machine generator instead. Ragel is one very good and robust solution.

http://www.complang.org/ragel/

kasperjj
A: 
switch(currentState) {
    state1:
         currentState = NEXT_STATE2;
         break;
    ....
}
Shaihi
+4  A: 
typedef std::pair<State,Message> StateMessagePair;
typedef std::map<StateMessagePair,State> StateDiagram;
StateDiagram sd;
// add logic to diagram
...
State currentState = getInitialState();
...
// process message
Message m = getMessage();
StateDiagram::iterator it=sd.find(std::make_pair<currentState,m>));
if (it==sd.end()) exit("incorrect message");
currentState = it->second;

EDIT: Building up the state diagram is done like this (example is for a cola-vending machine):

StateDiagram.insert(std::make_pair(State::Idle           ,Message::MakeChoice   ),State::WaitingForMoney);
StateDiagram.insert(std::make_pair(State::WaitingForMoney,Message::Cancel       ),State::Idle);
StateDiagram.insert(std::make_pair(State::WaitingForMoney,Message::MoneyEntered ),State::FindCan);
StateDiagram.insert(std::make_pair(State::FindCan        ,Message::CanSentToUser),State::Idle);

Default actions can be implemented using a second map, where the key is only the State, like this:

typedef std::map<State,State> StateDiagramForDefaults;

Instead of printing "incorrect message", the logic can perform a lookup in the StateDiagramForDefaults.

If actions needs to be added to the state diagram, the value of the map should be a pair consisting of an action, and a new state, like this:

typedef std::pair<State,Message> StateMessagePair;
typedef std::pair<State,IAction *> StateActionPair;
typedef std::map<StateMessagePair,StateActionPair> StateDiagram;

The logic that builds up the diagram should then "new" an instance of a class that implements IAction, and put that in the StateDiagram.

The executing logic then just executes the IAction implementation via a virtual method (e.g. execute() or the ()-operator).

Patrick
Few my colleagues use that way to build state machines (actually bit better: they do no use std::pair, but nested std::maps). It looks cool and OOP, but my problem is that in such implementations one looses sight on actually how the transition table looks like. Needless to mention that adding a default action for a state is a major pain.
Dummy00001
I think that the transition table becomes clearer instead of less clear. If you have one clear place where the pairs are added to the map, you also have a clear overview of all the transitions. And default actions can be implemented using a separate map. I'll clarify both things in my answer in a few moments.
Patrick
+2  A: 

One way is to use a class like this (rough example code ahead):

class State
{
  //pass a new Message into the current State
  //current State does (state-specific) processing of
  //the message, and returns a pointer to the new State
  //if there's a state change
  virtual State* getNewState(const Message&) = 0;
};

class ExampleState
{
  virtual State* getNewState(const Message& message)
  {
    switch (message.type)
    {
      case MessageType.Stop:
        //change state to stopped
        return new StoppedState();
    }
    //no state change
    return 0;
  }
};

On of the complications is whether states should be static flyweights, or whether they carry instance data and should therefore be newed and deleted.

ChrisW
Another possible complication is implementing super- and sub-states, which allows some messages to have the same processing in all substates by implementing the processing for that message in the superstate.
ChrisW
ChrisW
@ ChrisW, Thank you for answering. Your code is very interesting, and is very close to what I need. I think to write the complete program and post it here. Thanks again.
Igal Spector
A: 

Of course, boost have something in store for you: boost Statechart library. You can also find some nice tutorials there.

jopa
+3  A: 

The standard state machine implementation techniques are:

  1. the nested switch statement (some previous posts show examples of this technique)
  2. state-table
  3. the GoF State design pattern
  4. combination of the above

If you are new to state machines and implementation in C or C++, I would recommend my Dr.Dobbs article "Back to Basics" available at http://www.ddj.com/184401737 (you need to click on the Print link at the top to conveniently read the text.)

None of the standard techniques is suitable for the hierarchical state machines (such as UML statecharts). If you are interested in the modern UML state machines, I'd recommend my 3-part Embedded.com article "A crash course in UML state machines" (http://www.embedded.com/design/testissue/215801043).

Miro
@ Miro :Thanks for refering me to this website. Seems very interesting and informative. Thanks again.
Igal Spector
+1  A: 

See http://cs.atu.edu/~morell/classes/4163/notes/fa.html for several different ways of building finite state machines.

Larry Morell
@Larry Morell: Thanks for refering me to this website. Seems very interesting and informative. Thanks again.
Igal Spector
+1  A: 

You can use Ragel state machine compiler: http://www.complang.org/ragel/

Chris Dennett
A: 

the one i am using is based on this one

http://ehiti.de/machine_objects/ MACHine Objects

seems to be well optimized and is a hell of a lot less complex to use than boost Statechart

lurscher
@lurscher: Thanks for refering me to this website. Seems very interesting and informative. Thanks again.
Igal Spector
+1  A: 

Gosh, it isn’t as complicated as it seems. State machine code is very simple and short.

Coding a state machines is just about trivial. The hard part is designing a state machine that behaves correctly in all possible cases.

But let us assume that you have the correct design. How do you code it?

  1. Store the state in an attribute, myState perhaps.

  2. Whenever you receive a message switch on the myState attribute to execute the code for that state.

3 In each state, switch on the message to execute the code for that state AND that message

So you need a nested switch statement

cStateMachine::HandleMessage( msg_t msg )
{
  switch( myState ) {

     case A:

        switch( msg ) {

           case M:

           // here is the code to handle message M when in state A

...

Once you have this up and running, it is fun and easy to add more states and messages.

ravenspoint