BoostCon09Full.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. // Copyright 2010 Christophe Henry
  2. // henry UNDERSCORE christophe AT hotmail DOT com
  3. // This is an extended version of the state machine available in the boost::mpl library
  4. // Distributed under the same license as the original.
  5. // Copyright for the original version:
  6. // Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed
  7. // under the Boost Software License, Version 1.0. (See accompanying
  8. // file LICENSE_1_0.txt or copy at
  9. // http://www.boost.org/LICENSE_1_0.txt)
  10. #include <vector>
  11. #include <iostream>
  12. #include <boost/mpl/vector/vector50.hpp>
  13. #include <boost/msm/back/state_machine.hpp>
  14. #include <boost/msm/front/state_machine_def.hpp>
  15. #include <boost/msm/back/tools.hpp>
  16. namespace msm = boost::msm;
  17. namespace mpl = boost::mpl;
  18. namespace // Concrete FSM implementation
  19. {
  20. // events
  21. struct play {};
  22. struct end_pause {};
  23. struct stop {};
  24. struct pause {};
  25. struct open_close {};
  26. struct NextSong {};
  27. // event which every other event can convert to
  28. struct AllSongsPlayed
  29. {
  30. template <class Event>
  31. AllSongsPlayed(Event const&){}
  32. };
  33. struct PreviousSong {};
  34. struct error_found {};
  35. struct end_error {};
  36. // Flags. Allow information about a property of the current state
  37. struct CDLoaded {};
  38. struct FirstSongPlaying {};
  39. // A "complicated" event type that carries some data.
  40. enum DiskTypeEnum
  41. {
  42. DISK_CD=0,
  43. DISK_DVD=1
  44. };
  45. struct cd_detected
  46. {
  47. cd_detected(DiskTypeEnum diskType): disc_type(diskType) {}
  48. DiskTypeEnum disc_type;
  49. };
  50. // Concrete FSM implementation
  51. struct player_ : public msm::front::state_machine_def<player_>
  52. {
  53. // The list of FSM states
  54. struct Empty : public msm::front::state<>
  55. {
  56. typedef mpl::vector<play> deferred_events;
  57. // every (optional) entry/exit methods get the event packed as boost::any. Not useful very often.
  58. template <class Event,class FSM>
  59. void on_entry(Event const&,FSM& ) {std::cout << "entering: Empty" << std::endl;}
  60. template <class Event,class FSM>
  61. void on_exit(Event const&,FSM& ) {std::cout << "leaving: Empty" << std::endl;}
  62. };
  63. struct Open : public msm::front::state<>
  64. {
  65. typedef mpl::vector1<CDLoaded> flag_list;
  66. typedef mpl::vector<play> deferred_events;
  67. template <class Event,class FSM>
  68. void on_entry(Event const&,FSM& ) {std::cout << "entering: Open" << std::endl;}
  69. template <class Event,class FSM>
  70. void on_exit(Event const&,FSM& ) {std::cout << "leaving: Open" << std::endl;}
  71. };
  72. // a state needing a pointer to the containing state machine
  73. // and using for this the non-default policy
  74. // if policy used, set_sm_ptr is needed
  75. struct Stopped : public msm::front::state<default_base_state,msm::front::sm_ptr>
  76. {
  77. // when stopped, the CD is loaded
  78. typedef mpl::vector1<CDLoaded> flag_list;
  79. template <class Event,class FSM>
  80. void on_entry(Event const&,FSM& ) {std::cout << "entering: Stopped" << std::endl;}
  81. template <class Event,class FSM>
  82. void on_exit(Event const&,FSM& ) {std::cout << "leaving: Stopped" << std::endl;}
  83. void set_sm_ptr(player_* pl){m_player=pl;}
  84. player_* m_player;
  85. };
  86. // the player state machine contains a state which is himself a state machine
  87. // it demonstrates Shallow History: if the state gets activated with end_pause
  88. // then it will remember the last active state and reactivate it
  89. // also possible: AlwaysHistory, the last active state will always be reactivated
  90. // or NoHistory, always restart from the initial state
  91. struct Playing_ : public msm::front::state_machine_def<Playing_>
  92. {
  93. // when playing, the CD is loaded and we are in either pause or playing (duh)
  94. typedef mpl::vector<CDLoaded> flag_list;
  95. template <class Event,class FSM>
  96. void on_entry(Event const&,FSM& ) {std::cout << "entering: Playing" << std::endl;}
  97. template <class Event,class FSM>
  98. void on_exit(Event const&,FSM& ) {std::cout << "leaving: Playing" << std::endl;}
  99. // The list of FSM states
  100. struct Song1 : public msm::front::state<>
  101. {
  102. typedef mpl::vector1<FirstSongPlaying> flag_list;
  103. template <class Event,class FSM>
  104. void on_entry(Event const&,FSM& ) {std::cout << "starting: First song" << std::endl;}
  105. template <class Event,class FSM>
  106. void on_exit(Event const&,FSM& ) {std::cout << "finishing: First Song" << std::endl;}
  107. };
  108. struct Song2 : public msm::front::state<>
  109. {
  110. template <class Event,class FSM>
  111. void on_entry(Event const&,FSM& ) {std::cout << "starting: Second song" << std::endl;}
  112. template <class Event,class FSM>
  113. void on_exit(Event const&,FSM& ) {std::cout << "finishing: Second Song" << std::endl;}
  114. };
  115. struct Song3 : public msm::front::state<>
  116. {
  117. template <class Event,class FSM>
  118. void on_entry(Event const&,FSM& ) {std::cout << "starting: Third song" << std::endl;}
  119. template <class Event,class FSM>
  120. void on_exit(Event const&,FSM& ) {std::cout << "finishing: Third Song" << std::endl;}
  121. };
  122. struct CDFinished : public msm::front::exit_pseudo_state<AllSongsPlayed>
  123. {
  124. template <class Event,class FSM>
  125. void on_entry(Event const&,FSM& ) {std::cout << "entering: Playing::CDFinished" << std::endl;}
  126. template <class Event,class FSM>
  127. void on_exit(Event const&,FSM& ) {std::cout << "leaving: Playing::CDFinished" << std::endl;}
  128. };
  129. // the initial state. Must be defined
  130. typedef Song1 initial_state;
  131. // transition actions
  132. void start_next_song(NextSong const&) { std::cout << "Playing::start_next_song\n"; }
  133. void start_prev_song(PreviousSong const&) { std::cout << "Playing::start_prev_song\n"; }
  134. void all_songs_played(NextSong const&) { std::cout << "Playing::all_songs_played\n"; }
  135. // guard conditions
  136. typedef Playing_ pl; // makes transition table cleaner
  137. // Transition table for Playing
  138. struct transition_table : mpl::vector<
  139. // Start Event Next Action Guard
  140. // +---------+---------------+------------+---------------------+----------------------+
  141. a_row < Song1 , NextSong , Song2 , &pl::start_next_song >,
  142. a_row < Song2 , PreviousSong, Song1 , &pl::start_prev_song >,
  143. a_row < Song2 , NextSong , Song3 , &pl::start_next_song >,
  144. a_row < Song3 , PreviousSong, Song2 , &pl::start_prev_song >,
  145. a_row < Song3 , NextSong , CDFinished , &pl::all_songs_played >
  146. // +---------+---------------+---------+---------------------+----------------------+
  147. > {};
  148. // Replaces the default no-transition response.
  149. template <class FSM,class Event>
  150. void no_transition(Event const& e, FSM&,int state)
  151. {
  152. std::cout << "no transition from state " << state
  153. << " on event " << typeid(e).name() << std::endl;
  154. }
  155. };
  156. typedef msm::back::state_machine<Playing_,msm::back::ShallowHistory<mpl::vector<end_pause> > > Playing;
  157. // the player state machine contains a state which is himself a state machine (2 of them, Playing and Paused)
  158. struct Paused : public msm::front::state<>
  159. {
  160. typedef mpl::vector<CDLoaded> flag_list;
  161. template <class Event,class FSM>
  162. void on_entry(Event const&,FSM& ) {std::cout << "entering: Paused" << std::endl;}
  163. template <class Event,class FSM>
  164. void on_exit(Event const&,FSM& ) {std::cout << "leaving: Paused" << std::endl;}
  165. };
  166. struct AllOk : public msm::front::state<>
  167. {
  168. template <class Event,class FSM>
  169. void on_entry(Event const&,FSM& ) {std::cout << "starting: AllOk" << std::endl;}
  170. template <class Event,class FSM>
  171. void on_exit(Event const&,FSM& ) {std::cout << "finishing: AllOk" << std::endl;}
  172. };
  173. struct ErrorMode : //public msm::front::terminate_state<>
  174. public msm::front::interrupt_state<end_error>
  175. {
  176. template <class Event,class FSM>
  177. void on_entry(Event const&,FSM& ) {std::cout << "starting: ErrorMode" << std::endl;}
  178. template <class Event,class FSM>
  179. void on_exit(Event const&,FSM& ) {std::cout << "finishing: ErrorMode" << std::endl;}
  180. };
  181. // the initial state of the player SM. Must be defined
  182. typedef mpl::vector<Empty,AllOk> initial_state;
  183. // transition actions
  184. void start_playback(play const&) { std::cout << "player::start_playback\n"; }
  185. void open_drawer(open_close const&) { std::cout << "player::open_drawer\n"; }
  186. void close_drawer(open_close const&) { std::cout << "player::close_drawer\n"; }
  187. void store_cd_info(cd_detected const&)
  188. {
  189. std::cout << "player::store_cd_info\n";
  190. // generate another event to test the queue
  191. //cd.m_player.process_event(play());
  192. }
  193. void stop_playback(stop const&) { std::cout << "player::stop_playback\n"; }
  194. void end_playback (AllSongsPlayed const&) { std::cout << "player::end_playback\n"; }
  195. void pause_playback(pause const&) { std::cout << "player::pause_playback\n"; }
  196. void resume_playback(end_pause const&) { std::cout << "player::resume_playback\n"; }
  197. void stop_and_open(open_close const&) { std::cout << "player::stop_and_open\n"; }
  198. void stopped_again(stop const&){std::cout << "player::stopped_again\n";}
  199. void report_error(error_found const&) {std::cout << "player::report_error\n";}
  200. void report_end_error(end_error const&) {std::cout << "player::report_end_error\n";}
  201. // guard conditions
  202. bool good_disk_format(cd_detected const& evt)
  203. {
  204. // to test a guard condition, let's say we understand only CDs, not DVD
  205. if (evt.disc_type != DISK_CD)
  206. {
  207. std::cout << "wrong disk, sorry" << std::endl;
  208. return false;
  209. }
  210. return true;
  211. }
  212. typedef player_ p; // makes transition table cleaner
  213. // Transition table for player
  214. struct transition_table : mpl::vector<
  215. // Start Event Next Action Guard
  216. // +-------------+---------------+---------+---------------------+----------------------+
  217. a_row < Stopped , play , Playing , &p::start_playback >,
  218. a_row < Stopped , open_close , Open , &p::open_drawer >,
  219. a_row < Stopped , stop , Stopped , &p::stopped_again >,
  220. // +-------------+---------------+---------+---------------------+----------------------+
  221. a_row < Open , open_close , Empty , &p::close_drawer >,
  222. // +-------------+---------------+---------+---------------------+----------------------+
  223. a_row < Empty , open_close , Open , &p::open_drawer >,
  224. row < Empty , cd_detected , Stopped , &p::store_cd_info ,&p::good_disk_format >,
  225. // +-------------+---------------+---------+---------------------+----------------------+
  226. a_row < Playing , stop , Stopped , &p::stop_playback >,
  227. a_row < Playing , pause , Paused , &p::pause_playback >,
  228. a_row < Playing , open_close , Open , &p::stop_and_open >,
  229. a_row < Playing::exit_pt<
  230. Playing_::CDFinished> , AllSongsPlayed, Stopped , &p::end_playback >,
  231. // +-------------+---------------+---------+---------------------+----------------------+
  232. a_row < Paused , end_pause , Playing , &p::resume_playback >,
  233. a_row < Paused , stop , Stopped , &p::stop_playback >,
  234. a_row < Paused , open_close , Open , &p::stop_and_open >,
  235. // +-------------+---------------+---------+---------------------+----------------------+
  236. a_row < AllOk , error_found ,ErrorMode, &p::report_error >,
  237. a_row < ErrorMode ,end_error ,AllOk , &p::report_end_error >
  238. // +-------------+---------------+---------+---------------------+----------------------+
  239. > {};
  240. // Replaces the default no-transition response.
  241. template <class FSM,class Event>
  242. void no_transition(Event const& e, FSM&,int state)
  243. {
  244. std::cout << "no transition from state " << state
  245. << " on event " << typeid(e).name() << std::endl;
  246. }
  247. };
  248. typedef msm::back::state_machine<player_> player;
  249. //
  250. // Testing utilities.
  251. //
  252. void pstate(player const& p)
  253. {
  254. typedef player::stt Stt;
  255. typedef msm::back::generate_state_set<Stt>::type all_states;
  256. static char const* state_names[mpl::size<all_states>::value];
  257. // fill the names of the states defined in the state machine
  258. mpl::for_each<all_states,boost::msm::wrap<mpl::placeholders::_1> >
  259. (msm::back::fill_state_names<Stt>(state_names));
  260. for (unsigned int i=0;i<player::nr_regions::value;++i)
  261. {
  262. std::cout << " -> " << state_names[p.current_state()[i]] << std::endl;
  263. }
  264. }
  265. void test()
  266. {
  267. player p;
  268. // needed to start the highest-level SM. This will call on_entry and mark the start of the SM
  269. p.start();
  270. std::cout << "CDLoaded active:" << std::boolalpha << p.is_flag_active<CDLoaded>() << std::endl; //=> false (no CD yet)
  271. // test deferred event
  272. // deferred in Empty and Open, will be handled only after event cd_detected
  273. std::cout << "play is not handled in the current state but is marked as delayed" << std::endl;
  274. p.process_event(play()); pstate(p);
  275. std::cout << "cd_detected will cause play to be handled also" << std::endl;
  276. // will be rejected, wrong disk type
  277. p.process_event(cd_detected(DISK_DVD)); pstate(p);
  278. // will be accepted, wrong disk type
  279. p.process_event(cd_detected(DISK_CD)); pstate(p);
  280. std::cout << "FirstSong active:" << std::boolalpha << p.is_flag_active<FirstSongPlaying>() << std::endl; //=> true
  281. p.process_event(NextSong());pstate(p);
  282. // We are now in second song, Flag inactive
  283. std::cout << "FirstSong active:" << std::boolalpha << p.is_flag_active<FirstSongPlaying>() << std::endl;//=> false
  284. p.process_event(NextSong());pstate(p);
  285. // 2nd song active
  286. p.process_event(PreviousSong());pstate(p);
  287. // Pause
  288. p.process_event(pause()); pstate(p);
  289. // go back to Playing
  290. // but end_pause is an event activating the History
  291. // => keep the last active State (SecondSong)
  292. p.process_event(end_pause()); pstate(p);
  293. // force an exit by listening all the songs
  294. p.process_event(NextSong());
  295. p.process_event(NextSong());pstate(p);
  296. std::cout << "CDLoaded active:" << std::boolalpha << p.is_flag_active<CDLoaded>() << std::endl;//=> true
  297. std::cout << "FirstSong active:" << std::boolalpha << p.is_flag_active<FirstSongPlaying>() << std::endl;//=> false
  298. // go back to Playing
  299. // but play is not leading to Shallow History => do not remember the last active State (SecondSong)
  300. // and activate again FirstSong and LightOn
  301. p.process_event(play()); pstate(p);
  302. p.process_event(error_found()); pstate(p);
  303. // try generating more events
  304. std::cout << "Trying to generate another event" << std::endl; // will not work, fsm is terminated or interrupted
  305. p.process_event(NextSong());pstate(p);
  306. std::cout << "Trying to end the error" << std::endl; // will work only if ErrorMode is interrupt state
  307. p.process_event(end_error());pstate(p);
  308. std::cout << "Trying to generate another event" << std::endl; // will work only if ErrorMode is interrupt state
  309. p.process_event(NextSong());pstate(p);
  310. // the states and events of the higher level FSM (player)
  311. typedef player::stt Stt;
  312. typedef msm::back::generate_state_set<Stt>::type simple_states;
  313. std::cout << "the state list:" << std::endl;
  314. mpl::for_each<simple_states,boost::msm::wrap<mpl::placeholders::_1> >(msm::back::display_type ());
  315. std::cout << "the event list:" << std::endl;
  316. typedef msm::back::generate_event_set<Stt>::type event_list;
  317. mpl::for_each<event_list,boost::msm::wrap<mpl::placeholders::_1> >(msm::back::display_type ());
  318. std::cout << std::endl;
  319. // the states and events recursively searched
  320. typedef msm::back::recursive_get_transition_table<player>::type recursive_stt;
  321. std::cout << "the state list (including sub-SMs):" << std::endl;
  322. typedef msm::back::generate_state_set<recursive_stt>::type all_states;
  323. mpl::for_each<all_states,boost::msm::wrap<mpl::placeholders::_1> >(msm::back::display_type ());
  324. std::cout << "the event list (including sub-SMs):" << std::endl;
  325. typedef msm::back::generate_event_set<recursive_stt>::type all_events;
  326. mpl::for_each<all_events,boost::msm::wrap<mpl::placeholders::_1> >(msm::back::display_type ());
  327. }
  328. }
  329. int main()
  330. {
  331. test();
  332. return 0;
  333. }