SimpleInternalFunctors.cpp 15 KB

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