/* Copyright David Abrahams 2003-2004 Copyright Aleksey Gurtovoy 2003-2004 Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) This file was automatically extracted from the source of "C++ Template Metaprogramming", by David Abrahams and Aleksey Gurtovoy. It was built successfully with GCC 3.4.2 on Windows using the following command: g++ -I..\..\boost_1_32_0 -o%TEMP%\metaprogram-chapter11-example16.exe example16.cpp */ #include #include #include #include #include #include #include #include #include namespace mpl = boost::mpl; using namespace mpl::placeholders; #include template< class Transition , class Next > struct event_dispatcher { typedef typename Transition::fsm_t fsm_t; typedef typename Transition::event event; static int dispatch( fsm_t& fsm, int state, event const& e) { if (state == Transition::current_state) { Transition::execute(fsm, e); return Transition::next_state; } else // move on to the next node in the chain. { return Next::dispatch(fsm, state, e); } } }; template class state_machine; struct default_event_dispatcher { template static int dispatch( state_machine& m, int state, Event const& e) { return m.call_no_transition(state, e); } }; template struct generate_dispatcher; template class state_machine { // ... protected: template< int CurrentState , class Event , int NextState , void (Derived::*action)(Event const&) > struct row { // for later use by our metaprogram static int const current_state = CurrentState; static int const next_state = NextState; typedef Event event; typedef Derived fsm_t; // do the transition action. static void execute(Derived& fsm, Event const& e) { (fsm.*action)(e); } }; friend class default_event_dispatcher; template int call_no_transition(int state, Event const& e) { return static_cast(this) // CRTP downcast ->no_transition(state, e); } // public: 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->state = dispatcher::dispatch( *static_cast(this) // CRTP downcast , this->state , evt ); // return the new state return this->state; } // ... protected: state_machine() : state(Derived::initial_state) { } private: int state; // ... // ... public: template int no_transition(int state, Event const& e) { assert(false); return state; } // ... //// }; // get the Event associated with a transition. template struct transition_event { typedef typename Transition::event type; }; template struct generate_dispatcher : mpl::fold< mpl::filter_view< // select rows triggered by Event Table , boost::is_same > > , default_event_dispatcher , event_dispatcher<_2,_1> > {}; struct play {}; struct open_close {}; struct cd_detected { cd_detected(char const*, std::vector const&) {} }; #ifdef __GNUC__ // in which pause seems to have a predefined meaning # define pause pause_ #endif struct pause {}; struct stop {}; // concrete FSM implementation class player : public state_machine { private: // the list of FSM states enum states { Empty, Open, Stopped, Playing, Paused , initial_state = Empty }; #ifdef __MWERKS__ public: // Codewarrior bug workaround. Tested at 0x3202 #endif void start_playback(play const&); void open_drawer(open_close const&); void close_drawer(open_close const&); void store_cd_info(cd_detected const&); void stop_playback(stop const&); void pause_playback(pause const&); void resume_playback(play const&); void stop_and_open(open_close const&); #ifdef __MWERKS__ private: #endif friend class state_machine; typedef player p; // makes transition table cleaner // transition table struct transition_table : mpl::vector11< // Start Event Next Action // +---------+-------------+---------+---------------------+ row < Stopped , play , Playing , &p::start_playback >, row < Stopped , open_close , Open , &p::open_drawer >, // +---------+-------------+---------+---------------------+ row < Open , open_close , Empty , &p::close_drawer >, // +---------+-------------+---------+---------------------+ row < Empty , open_close , 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 , open_close , Open , &p::stop_and_open >, // +---------+-------------+---------+---------------------+ row < Paused , play , Playing , &p::resume_playback >, row < Paused , stop , Stopped , &p::stop_playback >, row < Paused , open_close , Open , &p::stop_and_open > // +---------+-------------+---------+---------------------+ > {}; typedef event_dispatcher< row , event_dispatcher< row , default_event_dispatcher > > dummy; }; void player::start_playback(play const&){} void player::open_drawer(open_close const&){} void player::close_drawer(open_close const&){} void player::store_cd_info(cd_detected const&){} void player::stop_playback(stop const&){} void player::pause_playback(pause const&){} void player::resume_playback(play const&){} void player::stop_and_open(open_close const&){} int main() { player p; // An instance of the FSM p.process_event(open_close()); // user opens CD player p.process_event(open_close()); // inserts CD and closes p.process_event( // CD is detected cd_detected( "louie, louie" , std::vector( /* track lengths */ ) ) ); p.process_event(play()); // etc. p.process_event(pause()); p.process_event(play()); p.process_event(stop()); return 0; }