SimpleWithFunctors.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  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 <iostream>
  11. // back-end
  12. #include <boost/msm/back/state_machine.hpp>
  13. //front-end
  14. #include <boost/msm/front/state_machine_def.hpp>
  15. // functors
  16. #include <boost/msm/front/functor_row.hpp>
  17. #include <boost/msm/front/euml/common.hpp>
  18. // for And_ operator
  19. #include <boost/msm/front/euml/operator.hpp>
  20. #ifndef BOOST_MSM_NONSTANDALONE_TEST
  21. #define BOOST_TEST_MODULE MyTest
  22. #endif
  23. #include <boost/test/unit_test.hpp>
  24. using namespace std;
  25. namespace msm = boost::msm;
  26. namespace mpl = boost::mpl;
  27. using namespace msm::front;
  28. // for And_ operator
  29. using namespace msm::front::euml;
  30. namespace
  31. {
  32. // events
  33. struct play {};
  34. struct end_pause {};
  35. struct stop {};
  36. struct pause {};
  37. struct open_close {};
  38. // A "complicated" event type that carries some data.
  39. enum DiskTypeEnum
  40. {
  41. DISK_CD=0,
  42. DISK_DVD=1
  43. };
  44. struct cd_detected
  45. {
  46. cd_detected(std::string name, DiskTypeEnum diskType)
  47. : name(name),
  48. disc_type(diskType)
  49. {}
  50. std::string name;
  51. DiskTypeEnum disc_type;
  52. };
  53. // front-end: define the FSM structure
  54. struct player_ : public msm::front::state_machine_def<player_>
  55. {
  56. unsigned int start_playback_counter;
  57. unsigned int can_close_drawer_counter;
  58. unsigned int test_fct_counter;
  59. player_():
  60. start_playback_counter(0),
  61. can_close_drawer_counter(0),
  62. test_fct_counter(0)
  63. {}
  64. // The list of FSM states
  65. struct Empty : public msm::front::state<>
  66. {
  67. template <class Event,class FSM>
  68. void on_entry(Event const&,FSM& ) {++entry_counter;}
  69. template <class Event,class FSM>
  70. void on_exit(Event const&,FSM& ) {++exit_counter;}
  71. int entry_counter;
  72. int exit_counter;
  73. };
  74. struct Open : public msm::front::state<>
  75. {
  76. template <class Event,class FSM>
  77. void on_entry(Event const&,FSM& ) {++entry_counter;}
  78. template <class Event,class FSM>
  79. void on_exit(Event const&,FSM& ) {++exit_counter;}
  80. int entry_counter;
  81. int exit_counter;
  82. };
  83. // sm_ptr still supported but deprecated as functors are a much better way to do the same thing
  84. struct Stopped : public msm::front::state<>
  85. {
  86. template <class Event,class FSM>
  87. void on_entry(Event const&,FSM& ) {++entry_counter;}
  88. template <class Event,class FSM>
  89. void on_exit(Event const&,FSM& ) {++exit_counter;}
  90. int entry_counter;
  91. int exit_counter;
  92. };
  93. struct Playing : public msm::front::state<>
  94. {
  95. template <class Event,class FSM>
  96. void on_entry(Event const&,FSM& ) {++entry_counter;}
  97. template <class Event,class FSM>
  98. void on_exit(Event const&,FSM& ) {++exit_counter;}
  99. int entry_counter;
  100. int exit_counter;
  101. };
  102. // state not defining any entry or exit
  103. struct Paused : public msm::front::state<>
  104. {
  105. template <class Event,class FSM>
  106. void on_entry(Event const&,FSM& ) {++entry_counter;}
  107. template <class Event,class FSM>
  108. void on_exit(Event const&,FSM& ) {++exit_counter;}
  109. int entry_counter;
  110. int exit_counter;
  111. };
  112. // the initial state of the player SM. Must be defined
  113. typedef Empty initial_state;
  114. // transition actions
  115. struct TestFct
  116. {
  117. template <class EVT,class FSM,class SourceState,class TargetState>
  118. void operator()(EVT const&, FSM& fsm,SourceState& ,TargetState& )
  119. {
  120. ++fsm.test_fct_counter;
  121. }
  122. };
  123. struct start_playback
  124. {
  125. template <class EVT,class FSM,class SourceState,class TargetState>
  126. void operator()(EVT const& ,FSM& fsm,SourceState& ,TargetState& )
  127. {
  128. ++fsm.start_playback_counter;
  129. }
  130. };
  131. struct open_drawer
  132. {
  133. template <class EVT,class FSM,class SourceState,class TargetState>
  134. void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& )
  135. {
  136. }
  137. };
  138. struct close_drawer
  139. {
  140. template <class EVT,class FSM,class SourceState,class TargetState>
  141. void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& )
  142. {
  143. }
  144. };
  145. struct store_cd_info
  146. {
  147. template <class EVT,class FSM,class SourceState,class TargetState>
  148. void operator()(EVT const&,FSM& fsm ,SourceState& ,TargetState& )
  149. {
  150. fsm.process_event(play());
  151. }
  152. };
  153. struct stop_playback
  154. {
  155. template <class EVT,class FSM,class SourceState,class TargetState>
  156. void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& )
  157. {
  158. }
  159. };
  160. struct pause_playback
  161. {
  162. template <class EVT,class FSM,class SourceState,class TargetState>
  163. void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& )
  164. {
  165. }
  166. };
  167. struct resume_playback
  168. {
  169. template <class EVT,class FSM,class SourceState,class TargetState>
  170. void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& )
  171. {
  172. }
  173. };
  174. struct stop_and_open
  175. {
  176. template <class EVT,class FSM,class SourceState,class TargetState>
  177. void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& )
  178. {
  179. }
  180. };
  181. struct stopped_again
  182. {
  183. template <class EVT,class FSM,class SourceState,class TargetState>
  184. void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& )
  185. {
  186. }
  187. };
  188. // guard conditions
  189. struct DummyGuard
  190. {
  191. template <class EVT,class FSM,class SourceState,class TargetState>
  192. bool operator()(EVT const&,FSM&,SourceState&,TargetState&)
  193. {
  194. return true;
  195. }
  196. };
  197. struct good_disk_format
  198. {
  199. template <class EVT,class FSM,class SourceState,class TargetState>
  200. bool operator()(EVT const& evt ,FSM&,SourceState& ,TargetState& )
  201. {
  202. // to test a guard condition, let's say we understand only CDs, not DVD
  203. if (evt.disc_type != DISK_CD)
  204. {
  205. return false;
  206. }
  207. return true;
  208. }
  209. };
  210. struct always_true
  211. {
  212. template <class EVT,class FSM,class SourceState,class TargetState>
  213. bool operator()(EVT const& ,FSM&,SourceState& ,TargetState& )
  214. {
  215. return true;
  216. }
  217. };
  218. struct can_close_drawer
  219. {
  220. template <class EVT,class FSM,class SourceState,class TargetState>
  221. bool operator()(EVT const& ,FSM& fsm,SourceState& ,TargetState& )
  222. {
  223. ++fsm.can_close_drawer_counter;
  224. return true;
  225. }
  226. };
  227. typedef player_ p; // makes transition table cleaner
  228. // Transition table for player
  229. struct transition_table : mpl::vector<
  230. // Start Event Next Action Guard
  231. // +---------+-------------+---------+---------------------+----------------------+
  232. Row < Stopped , play , Playing , ActionSequence_
  233. <mpl::vector<
  234. TestFct,start_playback> >
  235. , DummyGuard >,
  236. Row < Stopped , open_close , Open , open_drawer , none >,
  237. Row < Stopped , stop , Stopped , none , none >,
  238. // +---------+-------------+---------+---------------------------+----------------------+
  239. Row < Open , open_close , Empty , close_drawer , can_close_drawer >,
  240. // +---------+-------------+---------+---------------------------+----------------------+
  241. Row < Empty , open_close , Open , open_drawer , none >,
  242. Row < Empty , cd_detected , Stopped , store_cd_info , And_<good_disk_format,
  243. always_true> >,
  244. // +---------+-------------+---------+---------------------------+----------------------+
  245. Row < Playing , stop , Stopped , stop_playback , none >,
  246. Row < Playing , pause , Paused , pause_playback , none >,
  247. Row < Playing , open_close , Open , stop_and_open , none >,
  248. // +---------+-------------+---------+---------------------------+----------------------+
  249. Row < Paused , end_pause , Playing , resume_playback , none >,
  250. Row < Paused , stop , Stopped , stop_playback , none >,
  251. Row < Paused , open_close , Open , stop_and_open , none >
  252. // +---------+-------------+---------+---------------------+----------------------+
  253. > {};
  254. // Replaces the default no-transition response.
  255. template <class FSM,class Event>
  256. void no_transition(Event const&, FSM&,int)
  257. {
  258. BOOST_FAIL("no_transition called!");
  259. }
  260. // init counters
  261. template <class Event,class FSM>
  262. void on_entry(Event const&,FSM& fsm)
  263. {
  264. fsm.template get_state<player_::Stopped&>().entry_counter=0;
  265. fsm.template get_state<player_::Stopped&>().exit_counter=0;
  266. fsm.template get_state<player_::Open&>().entry_counter=0;
  267. fsm.template get_state<player_::Open&>().exit_counter=0;
  268. fsm.template get_state<player_::Empty&>().entry_counter=0;
  269. fsm.template get_state<player_::Empty&>().exit_counter=0;
  270. fsm.template get_state<player_::Playing&>().entry_counter=0;
  271. fsm.template get_state<player_::Playing&>().exit_counter=0;
  272. fsm.template get_state<player_::Paused&>().entry_counter=0;
  273. fsm.template get_state<player_::Paused&>().exit_counter=0;
  274. }
  275. };
  276. // Pick a back-end
  277. typedef msm::back::state_machine<player_> player;
  278. // static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" };
  279. BOOST_AUTO_TEST_CASE( my_test )
  280. {
  281. player p;
  282. p.start();
  283. BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 1,"Empty entry not called correctly");
  284. p.process_event(open_close());
  285. BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Open should be active"); //Open
  286. BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 1,"Empty exit not called correctly");
  287. BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().entry_counter == 1,"Open entry not called correctly");
  288. p.process_event(open_close());
  289. BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Empty should be active"); //Empty
  290. BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().exit_counter == 1,"Open exit not called correctly");
  291. BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 2,"Empty entry not called correctly");
  292. BOOST_CHECK_MESSAGE(p.can_close_drawer_counter == 1,"guard not called correctly");
  293. p.process_event(
  294. cd_detected("louie, louie",DISK_DVD));
  295. BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Empty should be active"); //Empty
  296. BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().exit_counter == 1,"Open exit not called correctly");
  297. BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 2,"Empty entry not called correctly");
  298. p.process_event(
  299. cd_detected("louie, louie",DISK_CD));
  300. BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing
  301. BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 2,"Empty exit not called correctly");
  302. BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 1,"Stopped entry not called correctly");
  303. BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 1,"Stopped exit not called correctly");
  304. BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 1,"Playing entry not called correctly");
  305. BOOST_CHECK_MESSAGE(p.start_playback_counter == 1,"action not called correctly");
  306. BOOST_CHECK_MESSAGE(p.test_fct_counter == 1,"action not called correctly");
  307. p.process_event(pause());
  308. BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active"); //Paused
  309. BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 1,"Playing exit not called correctly");
  310. BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().entry_counter == 1,"Paused entry not called correctly");
  311. // go back to Playing
  312. p.process_event(end_pause());
  313. BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing
  314. BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().exit_counter == 1,"Paused exit not called correctly");
  315. BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 2,"Playing entry not called correctly");
  316. p.process_event(pause());
  317. BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active"); //Paused
  318. BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 2,"Playing exit not called correctly");
  319. BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().entry_counter == 2,"Paused entry not called correctly");
  320. p.process_event(stop());
  321. BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped
  322. BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().exit_counter == 2,"Paused exit not called correctly");
  323. BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 2,"Stopped entry not called correctly");
  324. p.process_event(stop());
  325. BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped
  326. BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 2,"Stopped exit not called correctly");
  327. BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 3,"Stopped entry not called correctly");
  328. }
  329. }