// // Copyright 2003-2004 David Abrahams and Aleksey Gurtovoy. All Rights Reserved // #include "boost/mpl/int.hpp" #include "boost/mpl/fold.hpp" #include "boost/mpl/prior.hpp" #include "boost/mpl/count.hpp" #include "boost/mpl/insert.hpp" #include "boost/type_traits/is_same.hpp" #include "boost/mpl/vector/vector20.hpp" #include "boost/assert.hpp" #include #include #include #if defined(BOOST_DINKUMWARE_STDLIB) && BOOST_DINKUMWARE_STDLIB < 310 namespace std { using ::clock_t; } #endif namespace mpl = boost::mpl; using namespace mpl::placeholders; // Used when no transition has been defined for this state struct default_event_dispatcher { template static int dispatch(F& fsm, int state, Event const& e) { fsm.no_transition(e); // Default behavior asserts. Can be replaced in the derived FSM class return state; // If no assertion, we're staying put. } }; // event_dispatchers for each Event type are assembled in a chain. // Each node in the chain checks for one start state that has a // transition on the Event. With sufficient inlining/optimization the // dispatch function can be equivalent to the "obvious" case // statement. template< class Event , class Transition , class Next = default_event_dispatcher > struct event_dispatcher { typedef typename Transition::fsm_t fsm_t; static int dispatch( fsm_t& fsm, int state, Event const& evt) { if (state == Transition::from_state) { Transition::execute(fsm, evt); return Transition::to_state; } else // move on to the next node in the chain. { return Next::dispatch(fsm, state, evt); } } }; // A metafunction that returns the Event associated with a transition. template struct transition_event { typedef typename Transition::event type; }; // A metafunction that generates the dispatcher chain for a given // Event and Transitions table. template struct generate_dispatcher : mpl::fold< Transitions , default_event_dispatcher // Start with the no-transition handler , mpl::if_< boost::is_same > // If the transition's event matches , event_dispatcher // Add a new event_dispatcher node , _1 // Otherwise pass the previous chain through > > { }; // CRTP base class for state machines. Pass the actual FSM class as // the Derived parameter. template class state_machine { public: // Member functions // Main function used by clients of the derived FSM to make // transitions. template int process_event(Event const& evt) { // Generate the dispatcher type. typedef typename generate_dispatcher< typename Derived::transition_table, Event >::type dispatcher; // Dispatch the event. this->m_state = dispatcher::dispatch( *static_cast(this) , this->m_state , evt ); // Return the new state return this->m_state; } // Getter that returns the current state of the FSM int current_state() const { return this->m_state; } // Default no-transition handler. Can be replaced in the Derived // FSM class. template void no_transition(Event const& e) { BOOST_ASSERT(false); } protected: // interface for the derived class template state_machine(State state) // Construct with an initial state : m_state(state) { } state_machine() : m_state(Derived::initial_state) // Construct with the default initial_state { } // Template used to form rows in the transition table template< int From , class Event , int To , void (Derived::*action)(Event const&) > struct row { BOOST_STATIC_CONSTANT(int, from_state = From); BOOST_STATIC_CONSTANT(int, to_state = To); typedef Event event; typedef Derived fsm_t; // Do the transition action. static void execute(Derived& fsm, Event const& evt) { (fsm.*action)(evt); } }; private: int m_state; }; namespace // Concrete FSM implementation { // events struct play {}; struct stop {}; struct pause {}; struct drawer {}; // A "complicated" event type that carries some data. struct cd_detected { cd_detected(std::string name, std::vector durations) : name(name) , track_durations(durations) {} std::string name; std::vector track_durations; }; // Concrete FSM implementation class player : public state_machine { public: // Replaces the default no-transition response. template void no_transition(Event const& e) { std::cout << "no transition\n" << std::endl; } #if defined(__MWERKS__) // tested at 0x3003 public: #endif // transition actions void start_playback(play const&) { std::cout << "player::start_playback\n"; } void open_drawer(drawer const&) { std::cout << "player::open_drawer\n"; } void close_drawer(drawer const&) { std::cout << "player::close_drawer\n"; } void store_cd_info(cd_detected const&) { std::cout << "player::store_cd_info\n"; } void stop_playback(stop const&) { std::cout << "player::stop_playback\n"; } void pause_playback(pause const&) { std::cout << "player::pause_playback\n"; } void resume_playback(play const&) { std::cout << "player::resume_playback\n"; } void stop_and_open(drawer const&) { std::cout << "player::stop_and_open\n"; } private: friend class state_machine; // The list of FSM states enum states { empty, open, stopped, playing, paused , initial_state = empty }; typedef player p; // makes transition table cleaner // Transition table typedef mpl::vector11< // Source Event Target Action // +---------+-------------+---------+---------------------+ row < stopped , play , playing , &p::start_playback >, // +---------+-------------+---------+---------------------+ row < stopped , drawer , open , &p::open_drawer >, // +---------+-------------+---------+---------------------+ row < open , drawer , empty , &p::close_drawer >, // +---------+-------------+---------+---------------------+ row < empty , drawer , open , &p::open_drawer >, // +---------+-------------+---------+---------------------+ row < empty , cd_detected , stopped , &p::store_cd_info >, // +---------+-------------+---------+---------------------+ row < playing , stop , stopped , &p::stop_playback >, // +---------+-------------+---------+---------------------+ row < playing , pause , paused , &p::pause_playback >, // +---------+-------------+---------+---------------------+ row < playing , drawer , open , &p::stop_and_open >, // +---------+-------------+---------+---------------------+ row < paused , play , playing , &p::resume_playback >, // +---------+-------------+---------+---------------------+ row < paused , stop , stopped , &p::stop_playback >, // +---------+-------------+---------+---------------------+ row < paused , drawer , open , &p::stop_and_open > // +---------+-------------+---------+---------------------+ > transition_table; }; // // Testing utilities. // static char const* const state_names[] = { "empty", "open", "stopped", "playing", "paused" }; void pstate(player const& p) { std::cout << " -> " << state_names[p.current_state()] << std::endl; } void test() { player p; p.process_event(drawer()); pstate(p); p.process_event(drawer()); pstate(p); p.process_event( cd_detected( "louie, louie" , std::vector( /* track lengths */ ) ) ); pstate(p); p.process_event(play()); pstate(p); p.process_event(pause()); pstate(p); p.process_event(play()); pstate(p); p.process_event(stop()); pstate(p); } } int main() { test(); return 0; }