#include "stdafx.h" #include <iostream> #include <boost/mpl/fold.hpp> #include <boost/mpl/filter_view.hpp> #include <boost/type_traits/is_same.hpp> #include <boost/mpl/vector.hpp> #include <boost/mpl/placeholders.hpp> #include <boost/mpl/assert.hpp> #include <boost/static_assert.hpp> #include <vector> #include <ctime> #include <cassert> namespace mpl = boost::mpl; using namespace mpl::placeholders; //通过递归的“分派” template< class Transition , class Next > struct event_dispatcher { //Finite State Machines 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 Derived> class state_machine; //默认的转发,实际上是一个初始值。 struct default_event_dispatcher { template<class FSM, class Event> static int dispatch( state_machine<FSM>& m, int state, Event const& e) { return m.call_no_transition(state, e); } }; template<class Table, class Event> struct generate_dispatcher; template<class Derived> class state_machine { // ... protected: template< int CurrentState , class Event , int NextState , void (*action)(Derived&, Event const&) > struct row { // for later use by our metaprogram enum { current_state = CurrentState }; enum { next_state = NextState }; typedef Event event; typedef Derived fsm_t; // do the transition action. static void execute(Derived& fsm, Event const& e) { (*action)(fsm, e); } }; friend struct default_event_dispatcher; template <class Event> int call_no_transition(int state, Event const& e) { return static_cast<Derived*>(this)->no_transition(state, e); // CRTP downcast } // public: template<class Event> int process_event(Event const& evt) { // generate the dispatcher type. typedef typename generate_dispatcher< /*typename*/ Derived::transition_table, Event >::type dispatcher;//相同的"Event"为一个row // dispatch the event. //这儿来递归的调用。对于相同的"Event",用state来区分。 this->state = dispatcher::dispatch( *static_cast<Derived*>(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 <class Event> int no_transition(int state, Event const& e) { // assert(false); return state; } // ... //// }; //在元编程中的固定写法。用类封装。 template <class Event> struct is_same_event { template <class Transition> struct apply : boost::is_same<Event,typename Transition::event> {//is_same比较两者类型是否相同,如果一者有const,另外一个没有const,这样算不同的类型。 }; }; //is_same_event比较"event"是否相同 //filter_view,把相同的"event"存放在一起。 //fold对相同的这些"event"调用方法。 /* template<typename Sequence ,typename State ,typename ForwardOp >struct fold { typedef unspecified type; }; 描述持续调用二元 ForwardOp, 以上一次调用 ForwardOp 所得结果(第一次调用时使用 State)和区间 [begin<Sequence>::type, end<Sequence>::type) 的每个元素为参数, 返回最终的结果。 */ template<class Table, class Event> struct generate_dispatcher : mpl::fold< mpl::filter_view< // select rows triggered by Event Table , is_same_event<Event> > , default_event_dispatcher , event_dispatcher<_2,_1> > {}; //全例子最难的就是这个算法调用。上面的调用经过扩展,当参数是 //open_close()的时候 //event_dispatcher<state_machine<player>::row<4,open_close,1,&player::stop_and_open>, // event_dispatcher<state_machine<player>::row<3,open_close,1,&player::stop_and_open>, // event_dispatcher<state_machine<player>::row<0,open_close,1,&player::open_drawer>, // event_dispatcher<state_machine<player>::row<2,open_close,1,&player::open_drawer>,default_event_dispatcher>>>> //可以利用新建对象的方法来跟踪其类型。这是一个诀窍。 struct play {}; struct open_close {}; struct cd_detected { cd_detected(char const*, std::vector<clock_t> 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<player> { public: // 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 static void start_playback(player&, play const&); static void open_drawer(player&, open_close const&); static void close_drawer(player&, open_close const&); static void store_cd_info(player&, cd_detected const&); static void stop_playback(player&, stop const&); static void pause_playback(player&, pause const&); static void resume_playback(player&, play const&); static void stop_and_open(player&, open_close const&); #ifdef __MWERKS__ private: #endif friend class state_machine<player>; typedef player p; // makes transition table cleaner // transition table struct transition_table : mpl::vector< // Start Event Next Action // +---------+-------------+---------+------------------+ row < Stopped , play , Playing , &start_playback >, row < Stopped , open_close , Open , &open_drawer >, // +---------+-------------+---------+------------------+ row < Open , open_close , Empty , &close_drawer >, // +---------+-------------+---------+------------------+ row < Empty , open_close , Open , &open_drawer >, row < Empty , cd_detected , Stopped , &store_cd_info >, // +---------+-------------+---------+------------------+ row < Playing , stop , Stopped , &stop_playback >, row < Playing , pause , Paused , &pause_playback >, row < Playing , open_close , Open , &stop_and_open >, // +---------+-------------+---------+------------------+ row < Paused , play , Playing , &resume_playback >, row < Paused , stop , Stopped , &stop_playback >, row < Paused , open_close , Open , &stop_and_open > // +---------+-------------+---------+------------------+ > {}; typedef event_dispatcher< row<Stopped, play const, Playing, &start_playback> , event_dispatcher< row<Paused, play const, Playing, &resume_playback> , default_event_dispatcher > > dummy; }; void player::start_playback(player&, play const&) { std::cout<<"start_playback "<< std::endl; } void player::open_drawer(player&, open_close const&) { std::cout<<"open_drawer "<< std::endl; } void player::close_drawer(player&, open_close const&) { std::cout<<"close_drawer "<< std::endl; } void player::store_cd_info(player&, cd_detected const&) { std::cout<<"store_cd_info "<< std::endl; } void player::stop_playback(player&, stop const&) { std::cout<<"stop_playback "<< std::endl; } void player::pause_playback(player&, pause const&) { std::cout<<"pause_playback "<< std::endl; } void player::resume_playback(player&, play const&) { std::cout<<"resume_playback "<< std::endl; } void player::stop_and_open(player&, open_close const&) { std::cout<<"stop_and_open "<<std::endl; } int _tmain(int argc, _TCHAR* argv[]) { 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<clock_t>( /* track lengths */ ) ) ); p.process_event(play()); // etc. p.process_event(pause()); p.process_event(play()); p.process_event(stop()); return 0; }