===== Definition =====
/*
*
* Very simple template based FSM without any virtualness at all. Makes single use of
* a static_cast which i'm not 100% happy with, but speed was the most important thing
* along with code size (the real implementaion had 18 states and over 100 events).
*
* boost:statechart was far, far to complicated (and bloated) to use as a solution for the
* problem i was tacking, so came up with this instead.
*
* Note that this FSM is not in any way re-entrant - there can only be one thread of control
* in the FSM at any one point. Not only that, but only one call can be made to process_event()
* at a time - i.e, you can't call process_event again until is has returned. this means that any
* callbacks out of the FSM can not call process_event themselves. Failure to adhere to these
* requirements will result in an assert(false) witout NDEBUG defined, and undefined behviour
* if NDEBUG is defined.
*
* I find it easier to visiualise a concerete FSM implementation by handling the actions for
* states within the event implementation, rather than providing the actions for events within
* the state implementation. some may say this is bad design, but i prefer it - sorry :o)
*
* Note that i use auto_ptr instead of boost:shared_ptr for this example - you can just
* as easily use boost::shared_ptr or even a raw pointer.
*
* State storage can be accomplished by specialising the state template and adding the storage.
*
* If a state is terminated rather then moving to a new state, then the state machine
* will reset itself.
*
* Theo P. Zourzouvillys - 30/05/06
*
*/
#ifndef NDEBUG
#include
#endif
#include
#include
#include
#define STATE_MACHINE_DEFINE_EVENTS(sm_name) \
template<> template void sm_name::self_t::base_state::process_event(const EventTypeT & evt) { \
typedef sm_name sm_t; switch (m_state)
#define STATE_CASE(R, _, T) case T: evt(static_cast< sm_t::self_t::state< T > & >(*this)); return;
#define STATE_MACHINE_DEFINITION(machine, states) \
struct machine; \
STATE_MACHINE_DEFINE_EVENTS(machine) { BOOST_PP_LIST_FOR_EACH(STATE_CASE, _, states) } }
/*
* -------- State machine definition --------
*/
template
struct state_machine
{
public:
typedef state_machine< DerivedT, StateTypeT, InitialStateT > self_t;
protected:
state_machine();
public:
template struct state;
protected:
struct base_state
{
base_state(StateTypeT state_, DerivedT & fsm_);
// The main worker for static_cast'ing - defined via STATE_MACHINE_DEFINITION()
template
void process_event(const T & e);
/*
* Transition to a new state.
*/
template
void transit();
/*
* Terminate this FSM. This essentially resets the FSM to it's inital state.
* ensure you do not use the state or state_machine after calling this function!
*/
void terminate();
private:
StateTypeT m_state;
DerivedT & m_fsm;
};
public:
template
struct state : public base_state
{
state(DerivedT & fsm_) : base_state(StateValueT, fsm_) {}
};
private:
void terminate();
template
void transit();
public:
template
void
process_event(const T & e);
private:
bool __processing;
std::auto_ptr< base_state > m_fsm;
};
===== Implementation =====
/* -------- State machine implementation -------- */
template
state_machine::state_machine()
: __processing(false)
{
}
template
inline void
state_machine::terminate()
{
m_fsm.reset();
}
template
template
inline void
state_machine::transit()
{
m_fsm = std::auto_ptr< base_state >(new state(static_cast(*this)));
}
template
template
inline void
state_machine::process_event(const T & e)
{
assert (__processing == false);
__processing = true;
try
{
if (m_fsm.get() == 0)
{
m_fsm = std::auto_ptr< base_state >(new state(static_cast(*this)));
}
m_fsm->process_event(e);
__processing = false;
}
catch (...)
{
__processing = false;
throw;
}
}
template
state_machine::base_state::base_state(StateTypeT state_, DerivedT & fsm_)
: m_state(state_), m_fsm(fsm_)
{
}
template
template
inline void
state_machine::base_state::transit()
{
m_fsm.transit();
}
template
inline void
state_machine::base_state::terminate()
{
m_fsm.terminate();
}
===== Example Usage =====
/* -------- Start Declaration of Example FSM -------- */
/*
* The states our FSM can be in. In this example, we have 3.
*/
typedef enum { INITIAL, PROV_IN, PROV_OUT } fsm_states_t;
struct sig_fsm_machine : public state_machine
{
};
/* State specialisations */
template<> template<> struct
sig_fsm_machine::self_t::state< PROV_IN > : public sig_fsm_machine::self_t::base_state
{
state(sig_fsm_machine & fsm_) : base_state(PROV_IN, fsm_) {}
};
/*
* Magic for static_cast. First option is the name of your concrete FSM class (defined above), and the
* second option is the states that the FSM can be in. Make sure you update the first parameters
* to BOOST_PP_TUPLE_TO_LIST (which is the number of states) when adding/removing state names.
*/
STATE_MACHINE_DEFINITION(
sig_fsm_machine,
BOOST_PP_TUPLE_TO_LIST(3, (INITIAL, PROV_IN, PROV_OUT))
)
/* -------- End Of Declaration -------- */
/*
* Example Usage
*/
/*
* Note that the event does not inherit from anything, so it is
* possible for an event to traverse multiple FSM's.
*/
struct MyEvent
{
void operator()(sig_fsm_machine::state & sm) const
{
// Transition to a new state:
sm.transit();
}
void operator()(sig_fsm_machine::state& sm) const
{
// Terminate the state.
sm.terminate();
}
template
void operator()(sig_fsm_machine::state & /* sm */) const
{
assert(0);
}
};
int main()
{
sig_fsm_machine fsm;
for (unsigned int i = 0 ; i < 1000000 ; i++) {
// Here we post an event 'MyEvent()'.
fsm.process_event(MyEvent());
}
}