#include "stdafx.h"<br />#include <iostream><br />#include <boost/mpl/fold.hpp><br />#include <boost/mpl/filter_view.hpp><br />#include <boost/type_traits/is_same.hpp><br />#include <boost/mpl/vector.hpp><br />#include <boost/mpl/placeholders.hpp><br />#include <boost/mpl/assert.hpp><br />#include <boost/static_assert.hpp><br />#include <vector><br />#include <ctime><br />#include <cassert></p><p>namespace mpl = boost::mpl;<br />using namespace mpl::placeholders;</p><p>//通過遞迴的“指派”<br />template<<br />class Transition<br />, class Next<br />><br />struct event_dispatcher<br />{ //Finite State Machines<br />typedef typename Transition::fsm_t fsm_t;<br />typedef typename Transition::event event;</p><p>static int dispatch(<br />fsm_t& fsm, int state, event const& e)<br />{<br />if (state == Transition::current_state)<br />{<br />Transition::execute(fsm, e);<br />return Transition::next_state;<br />}<br />else // move on to the next node in the chain.<br />{<br />return Next::dispatch(fsm, state, e);<br />}<br />}</p><p>};</p><p>template <class Derived> class state_machine;<br />//預設的轉寄,實際上是一個初始值。<br />struct default_event_dispatcher<br />{<br />template<class FSM, class Event><br />static int dispatch(<br />state_machine<FSM>& m, int state, Event const& e)<br />{<br />return m.call_no_transition(state, e);<br />}<br />};</p><p>template<class Table, class Event><br />struct generate_dispatcher;</p><p>template<class Derived><br />class state_machine<br />{<br />// ...<br />protected:<br />template<<br />int CurrentState<br />, class Event<br />, int NextState<br />, void (*action)(Derived&, Event const&)<br />><br />struct row<br />{<br />// for later use by our metaprogram<br />enum { current_state = CurrentState };<br />enum { next_state = NextState };<br />typedef Event event;<br />typedef Derived fsm_t;</p><p>// do the transition action.<br />static void execute(Derived& fsm, Event const& e)<br />{<br />(*action)(fsm, e);<br />}<br />};</p><p>friend struct default_event_dispatcher;</p><p>template <class Event><br />int call_no_transition(int state, Event const& e)<br />{<br />return static_cast<Derived*>(this)->no_transition(state, e); // CRTP downcast</p><p>}<br />//<br />public:<br />template<class Event><br />int process_event(Event const& evt)<br />{<br />// generate the dispatcher type.<br />typedef typename generate_dispatcher<<br />/*typename*/ Derived::transition_table, Event<br />>::type dispatcher;//相同的"Event"為一個row</p><p>// dispatch the event.<br />//這兒來遞迴的調用。對於相同的"Event",用state來區分。<br />this->state = dispatcher::dispatch(<br />*static_cast<Derived*>(this) // CRTP downcast<br />, this->state<br />, evt<br />);</p><p>// return the new state<br />return this->state;<br />}</p><p>// ...<br />protected:<br />state_machine()<br />: state(Derived::initial_state)<br />{<br />}</p><p>private:<br />int state;<br />// ...</p><p>// ...<br />public:<br />template <class Event><br />int no_transition(int state, Event const& e)<br />{<br />//assert(false);<br />return state;<br />}<br />// ...<br />////<br />};</p><p>//在元編程中的固定寫法。用類封裝。<br />template <class Event><br />struct is_same_event<br />{<br />template <class Transition> struct apply<br />: boost::is_same<Event,typename Transition::event><br />{//is_same比較兩者類型是否相同,如果一者有const,另外一個沒有const,這樣算不同的類型。<br />};<br />};<br />//is_same_event比較"event"是否相同<br />//filter_view,把相同的"event"存放在一起。<br />//fold對相同的這些"event"調用方法。<br />/*<br />template<typename Sequence<br /> ,typename State<br />,typename ForwardOp<br /> >struct fold<br />{<br />typedef unspecified type;<br />};<br />描述持續調用二元 ForwardOp,<br />以上一次調用 ForwardOp 所得結果(第一次調用時使用 State)和區間<br />[begin<Sequence>::type, end<Sequence>::type) 的每個元素為參數,<br />返回最終的結果。<br />*/</p><p>template<class Table, class Event><br />struct generate_dispatcher<br />: mpl::fold<<br />mpl::filter_view< // select rows triggered by Event<br />Table<br />, is_same_event<Event><br />><br />, default_event_dispatcher<br />, event_dispatcher<_2,_1><br />><br />{};<br />//全例子最難的就是這個演算法調用。上面的調用經過擴充,當參數是<br />//open_close()的時候<br />//event_dispatcher<state_machine<player>::row<4,open_close,1,&player::stop_and_open>,<br />//event_dispatcher<state_machine<player>::row<3,open_close,1,&player::stop_and_open>,<br />// event_dispatcher<state_machine<player>::row<0,open_close,1,&player::open_drawer>,<br />// event_dispatcher<state_machine<player>::row<2,open_close,1,&player::open_drawer>,default_event_dispatcher>>>><br />//可以利用建立對象的方法來跟蹤其類型。這是一個訣竅。</p><p>struct play {};<br />struct open_close {};<br />struct cd_detected {<br />cd_detected(char const*, std::vector<clock_t> const&) {}<br />};<br />#ifdef __GNUC__ // in which pause seems to have a predefined meaning<br /># define pause pause_<br />#endif<br />struct pause {};<br />struct stop {};</p><p>// concrete FSM implementation<br />class player : public state_machine<player><br />{<br />public:<br />// the list of FSM states<br />enum states {<br />Empty, Open, Stopped, Playing, Paused<br />, initial_state = Empty<br />};</p><p>#ifdef __MWERKS__<br />public: // Codewarrior bug workaround. Tested at 0x3202<br />#endif</p><p>static void start_playback(player&, play const&);<br />static void open_drawer(player&, open_close const&);<br />static void close_drawer(player&, open_close const&);<br />static void store_cd_info(player&, cd_detected const&);<br />static void stop_playback(player&, stop const&);<br />static void pause_playback(player&, pause const&);<br />static void resume_playback(player&, play const&);<br />static void stop_and_open(player&, open_close const&);</p><p>#ifdef __MWERKS__<br />private:<br />#endif<br />friend class state_machine<player>;<br />typedef player p; // makes transition table cleaner</p><p>// transition table<br />struct transition_table : mpl::vector<</p><p>// Start Event Next Action<br />// +---------+-------------+---------+------------------+<br />row < Stopped , play , Playing , &start_playback >,<br />row < Stopped , open_close , Open , &open_drawer >,<br />// +---------+-------------+---------+------------------+<br />row < Open , open_close , Empty , &close_drawer >,<br />// +---------+-------------+---------+------------------+<br />row < Empty , open_close , Open , &open_drawer >,<br />row < Empty , cd_detected , Stopped , &store_cd_info >,<br />// +---------+-------------+---------+------------------+<br />row < Playing , stop , Stopped , &stop_playback >,<br />row < Playing , pause , Paused , &pause_playback >,<br />row < Playing , open_close , Open , &stop_and_open >,<br />// +---------+-------------+---------+------------------+<br />row < Paused , play , Playing , &resume_playback >,<br />row < Paused , stop , Stopped , &stop_playback >,<br />row < Paused , open_close , Open , &stop_and_open ><br />// +---------+-------------+---------+------------------+</p><p>> {};<br />typedef</p><p>event_dispatcher<<br />row<Stopped, play const, Playing, &start_playback><br />, event_dispatcher<<br />row<Paused, play const, Playing, &resume_playback><br />, default_event_dispatcher<br />><br />><br />dummy;<br />};</p><p>void player::start_playback(player&, play const&)<br />{<br />std::cout<<"start_playback "<< std::endl;<br />}</p><p>void player::open_drawer(player&, open_close const&)<br />{<br />std::cout<<"open_drawer "<< std::endl;<br />}</p><p>void player::close_drawer(player&, open_close const&)<br />{<br />std::cout<<"close_drawer "<< std::endl;<br />}<br />void player::store_cd_info(player&, cd_detected const&)<br />{<br />std::cout<<"store_cd_info "<< std::endl;<br />}</p><p>void player::stop_playback(player&, stop const&)<br />{<br />std::cout<<"stop_playback "<< std::endl;<br />}</p><p>void player::pause_playback(player&, pause const&)<br />{<br />std::cout<<"pause_playback "<< std::endl;<br />}</p><p>void player::resume_playback(player&, play const&)<br />{<br />std::cout<<"resume_playback "<< std::endl;<br />}</p><p>void player::stop_and_open(player&, open_close const&)<br />{<br />std::cout<<"stop_and_open "<<std::endl;<br />}</p><p>int _tmain(int argc, _TCHAR* argv[])<br />{<br />player p; // An instance of the FSM</p><p>p.process_event(open_close()); // user opens CD player<br />p.process_event(open_close()); // inserts CD and closes<br />p.process_event( // CD is detected<br />cd_detected(<br />"Louie, Louie"<br />, std::vector<clock_t>( /* track lengths */ )<br />)<br />);<br />p.process_event(play()); // etc.<br />p.process_event(pause());<br />p.process_event(play());<br />p.process_event(stop());</p><p>return 0;<br />}