player1.cpp 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. /*
  2. Copyright David Abrahams 2003-2004
  3. Copyright Aleksey Gurtovoy 2003-2004
  4. Distributed under the Boost Software License, Version 1.0.
  5. (See accompanying file LICENSE_1_0.txt or copy at
  6. http://www.boost.org/LICENSE_1_0.txt)
  7. This file was automatically extracted from the source of
  8. "C++ Template Metaprogramming", by David Abrahams and
  9. Aleksey Gurtovoy.
  10. It was built successfully with GCC 3.4.2 on Windows using
  11. the following command:
  12. g++ -I..\..\boost_1_32_0 -o%TEMP%\metaprogram-chapter11-example16.exe example16.cpp
  13. */
  14. #include <boost/mpl/fold.hpp>
  15. #include <boost/mpl/filter_view.hpp>
  16. #include <boost/type_traits/is_same.hpp>
  17. #include <vector>
  18. #include <ctime>
  19. #include <boost/mpl/vector.hpp>
  20. #include <boost/mpl/placeholders.hpp>
  21. #include <boost/mpl/assert.hpp>
  22. #include <boost/static_assert.hpp>
  23. namespace mpl = boost::mpl;
  24. using namespace mpl::placeholders;
  25. #include <cassert>
  26. template<
  27. class Transition
  28. , class Next
  29. >
  30. struct event_dispatcher
  31. {
  32. typedef typename Transition::fsm_t fsm_t;
  33. typedef typename Transition::event event;
  34. static int dispatch(
  35. fsm_t& fsm, int state, event const& e)
  36. {
  37. if (state == Transition::current_state)
  38. {
  39. Transition::execute(fsm, e);
  40. return Transition::next_state;
  41. }
  42. else // move on to the next node in the chain.
  43. {
  44. return Next::dispatch(fsm, state, e);
  45. }
  46. }
  47. };
  48. template <class Derived> class state_machine;
  49. struct default_event_dispatcher
  50. {
  51. template<class FSM, class Event>
  52. static int dispatch(
  53. state_machine<FSM>& m, int state, Event const& e)
  54. {
  55. return m.call_no_transition(state, e);
  56. }
  57. };
  58. template<class Table, class Event>
  59. struct generate_dispatcher;
  60. template<class Derived>
  61. class state_machine
  62. {
  63. // ...
  64. protected:
  65. template<
  66. int CurrentState
  67. , class Event
  68. , int NextState
  69. , void (Derived::*action)(Event const&)
  70. >
  71. struct row
  72. {
  73. // for later use by our metaprogram
  74. static int const current_state = CurrentState;
  75. static int const next_state = NextState;
  76. typedef Event event;
  77. typedef Derived fsm_t;
  78. // do the transition action.
  79. static void execute(Derived& fsm, Event const& e)
  80. {
  81. (fsm.*action)(e);
  82. }
  83. };
  84. friend class default_event_dispatcher;
  85. template <class Event>
  86. int call_no_transition(int state, Event const& e)
  87. {
  88. return static_cast<Derived*>(this) // CRTP downcast
  89. ->no_transition(state, e);
  90. }
  91. //
  92. public:
  93. template<class Event>
  94. int process_event(Event const& evt)
  95. {
  96. // generate the dispatcher type.
  97. typedef typename generate_dispatcher<
  98. typename Derived::transition_table, Event
  99. >::type dispatcher;
  100. // dispatch the event.
  101. this->state = dispatcher::dispatch(
  102. *static_cast<Derived*>(this) // CRTP downcast
  103. , this->state
  104. , evt
  105. );
  106. // return the new state
  107. return this->state;
  108. }
  109. // ...
  110. protected:
  111. state_machine()
  112. : state(Derived::initial_state)
  113. {
  114. }
  115. private:
  116. int state;
  117. // ...
  118. // ...
  119. public:
  120. template <class Event>
  121. int no_transition(int state, Event const& e)
  122. {
  123. assert(false);
  124. return state;
  125. }
  126. // ...
  127. ////
  128. };
  129. // get the Event associated with a transition.
  130. template <class Transition>
  131. struct transition_event
  132. {
  133. typedef typename Transition::event type;
  134. };
  135. template<class Table, class Event>
  136. struct generate_dispatcher
  137. : mpl::fold<
  138. mpl::filter_view< // select rows triggered by Event
  139. Table
  140. , boost::is_same<Event, transition_event<_1> >
  141. >
  142. , default_event_dispatcher
  143. , event_dispatcher<_2,_1>
  144. >
  145. {};
  146. struct play {};
  147. struct open_close {};
  148. struct cd_detected {
  149. cd_detected(char const*, std::vector<std::clock_t> const&) {}
  150. };
  151. #ifdef __GNUC__ // in which pause seems to have a predefined meaning
  152. # define pause pause_
  153. #endif
  154. struct pause {};
  155. struct stop {};
  156. // concrete FSM implementation
  157. class player : public state_machine<player>
  158. {
  159. private:
  160. // the list of FSM states
  161. enum states {
  162. Empty, Open, Stopped, Playing, Paused
  163. , initial_state = Empty
  164. };
  165. #ifdef __MWERKS__
  166. public: // Codewarrior bug workaround. Tested at 0x3202
  167. #endif
  168. void start_playback(play const&);
  169. void open_drawer(open_close const&);
  170. void close_drawer(open_close const&);
  171. void store_cd_info(cd_detected const&);
  172. void stop_playback(stop const&);
  173. void pause_playback(pause const&);
  174. void resume_playback(play const&);
  175. void stop_and_open(open_close const&);
  176. #ifdef __MWERKS__
  177. private:
  178. #endif
  179. friend class state_machine<player>;
  180. typedef player p; // makes transition table cleaner
  181. // transition table
  182. struct transition_table : mpl::vector11<
  183. // Start Event Next Action
  184. // +---------+-------------+---------+---------------------+
  185. row < Stopped , play , Playing , &p::start_playback >,
  186. row < Stopped , open_close , Open , &p::open_drawer >,
  187. // +---------+-------------+---------+---------------------+
  188. row < Open , open_close , Empty , &p::close_drawer >,
  189. // +---------+-------------+---------+---------------------+
  190. row < Empty , open_close , Open , &p::open_drawer >,
  191. row < Empty , cd_detected , Stopped , &p::store_cd_info >,
  192. // +---------+-------------+---------+---------------------+
  193. row < Playing , stop , Stopped , &p::stop_playback >,
  194. row < Playing , pause , Paused , &p::pause_playback >,
  195. row < Playing , open_close , Open , &p::stop_and_open >,
  196. // +---------+-------------+---------+---------------------+
  197. row < Paused , play , Playing , &p::resume_playback >,
  198. row < Paused , stop , Stopped , &p::stop_playback >,
  199. row < Paused , open_close , Open , &p::stop_and_open >
  200. // +---------+-------------+---------+---------------------+
  201. > {};
  202. typedef
  203. event_dispatcher<
  204. row<Stopped, play, Playing, &player::start_playback>
  205. , event_dispatcher<
  206. row<Paused, play, Playing, &player::resume_playback>
  207. , default_event_dispatcher
  208. >
  209. >
  210. dummy;
  211. };
  212. void player::start_playback(play const&){}
  213. void player::open_drawer(open_close const&){}
  214. void player::close_drawer(open_close const&){}
  215. void player::store_cd_info(cd_detected const&){}
  216. void player::stop_playback(stop const&){}
  217. void player::pause_playback(pause const&){}
  218. void player::resume_playback(play const&){}
  219. void player::stop_and_open(open_close const&){}
  220. int main()
  221. {
  222. player p; // An instance of the FSM
  223. p.process_event(open_close()); // user opens CD player
  224. p.process_event(open_close()); // inserts CD and closes
  225. p.process_event( // CD is detected
  226. cd_detected(
  227. "louie, louie"
  228. , std::vector<std::clock_t>( /* track lengths */ )
  229. )
  230. );
  231. p.process_event(play()); // etc.
  232. p.process_event(pause());
  233. p.process_event(play());
  234. p.process_event(stop());
  235. return 0;
  236. }