Serialize.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  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. #ifndef BOOST_MSM_NONSTANDALONE_TEST
  16. #define BOOST_TEST_MODULE MyTest
  17. #endif
  18. #include <boost/test/unit_test.hpp>
  19. // include headers that implement a archive in simple text format
  20. #include <boost/archive/text_oarchive.hpp>
  21. #include <boost/archive/text_iarchive.hpp>
  22. #include <boost/serialization/tracking.hpp>
  23. #include <fstream>
  24. namespace msm = boost::msm;
  25. namespace mpl = boost::mpl;
  26. namespace
  27. {
  28. // events
  29. struct play {};
  30. struct end_pause {};
  31. struct stop {};
  32. struct pause {};
  33. struct open_close {};
  34. // A "complicated" event type that carries some data.
  35. enum DiskTypeEnum
  36. {
  37. DISK_CD=0,
  38. DISK_DVD=1
  39. };
  40. struct cd_detected
  41. {
  42. cd_detected(std::string name, DiskTypeEnum diskType)
  43. : name(name),
  44. disc_type(diskType)
  45. {}
  46. std::string name;
  47. DiskTypeEnum disc_type;
  48. };
  49. // front-end: define the FSM structure
  50. struct player_ : public msm::front::state_machine_def<player_>
  51. {
  52. unsigned int start_playback_counter;
  53. unsigned int can_close_drawer_counter;
  54. int front_end_data;
  55. player_():
  56. start_playback_counter(0),
  57. can_close_drawer_counter(0),
  58. front_end_data(4)
  59. {}
  60. //we want to serialize some data contained by the front-end
  61. // to achieve this, ask for it
  62. typedef int do_serialize;
  63. // and provide a serialize
  64. template<class Archive>
  65. void serialize(Archive & ar, const unsigned int )
  66. {
  67. ar & front_end_data;
  68. }
  69. // The list of FSM states
  70. struct Empty : public msm::front::state<>
  71. {
  72. template <class Event,class FSM>
  73. void on_entry(Event const&,FSM& ) {++entry_counter;}
  74. template <class Event,class FSM>
  75. void on_exit(Event const&,FSM& ) {++exit_counter;}
  76. int entry_counter;
  77. int exit_counter;
  78. int some_dummy_data;
  79. // we want Empty to be serialized
  80. typedef int do_serialize;
  81. template<class Archive>
  82. void serialize(Archive & ar, const unsigned int )
  83. {
  84. ar & some_dummy_data;
  85. }
  86. };
  87. struct Open : public msm::front::state<>
  88. {
  89. template <class Event,class FSM>
  90. void on_entry(Event const&,FSM& ) {++entry_counter;}
  91. template <class Event,class FSM>
  92. void on_exit(Event const&,FSM& ) {++exit_counter;}
  93. int entry_counter;
  94. int exit_counter;
  95. };
  96. // sm_ptr still supported but deprecated as functors are a much better way to do the same thing
  97. struct Stopped : public msm::front::state<>
  98. {
  99. template <class Event,class FSM>
  100. void on_entry(Event const&,FSM& ) {++entry_counter;}
  101. template <class Event,class FSM>
  102. void on_exit(Event const&,FSM& ) {++exit_counter;}
  103. int entry_counter;
  104. int exit_counter;
  105. };
  106. struct Playing : public msm::front::state<>
  107. {
  108. template <class Event,class FSM>
  109. void on_entry(Event const&,FSM& ) {++entry_counter;}
  110. template <class Event,class FSM>
  111. void on_exit(Event const&,FSM& ) {++exit_counter;}
  112. int entry_counter;
  113. int exit_counter;
  114. };
  115. // state not defining any entry or exit
  116. struct Paused : public msm::front::state<>
  117. {
  118. template <class Event,class FSM>
  119. void on_entry(Event const&,FSM& ) {++entry_counter;}
  120. template <class Event,class FSM>
  121. void on_exit(Event const&,FSM& ) {++exit_counter;}
  122. int entry_counter;
  123. int exit_counter;
  124. };
  125. // the initial state of the player SM. Must be defined
  126. typedef Empty initial_state;
  127. // transition actions
  128. void start_playback(play const&) {++start_playback_counter; }
  129. void open_drawer(open_close const&) { }
  130. void store_cd_info(cd_detected const&) { }
  131. void stop_playback(stop const&) { }
  132. void pause_playback(pause const&) { }
  133. void resume_playback(end_pause const&) { }
  134. void stop_and_open(open_close const&) { }
  135. void stopped_again(stop const&){}
  136. // guard conditions
  137. bool good_disk_format(cd_detected const& evt)
  138. {
  139. // to test a guard condition, let's say we understand only CDs, not DVD
  140. if (evt.disc_type != DISK_CD)
  141. {
  142. return false;
  143. }
  144. return true;
  145. }
  146. bool can_close_drawer(open_close const&)
  147. {
  148. ++can_close_drawer_counter;
  149. return true;
  150. }
  151. typedef player_ p; // makes transition table cleaner
  152. // Transition table for player
  153. struct transition_table : mpl::vector<
  154. // Start Event Next Action Guard
  155. // +---------+-------------+---------+---------------------+----------------------+
  156. a_row < Stopped , play , Playing , &p::start_playback >,
  157. a_row < Stopped , open_close , Open , &p::open_drawer >,
  158. _row < Stopped , stop , Stopped >,
  159. // +---------+-------------+---------+---------------------+----------------------+
  160. g_row < Open , open_close , Empty , &p::can_close_drawer >,
  161. // +---------+-------------+---------+---------------------+----------------------+
  162. a_row < Empty , open_close , Open , &p::open_drawer >,
  163. row < Empty , cd_detected , Stopped , &p::store_cd_info ,&p::good_disk_format >,
  164. // +---------+-------------+---------+---------------------+----------------------+
  165. a_row < Playing , stop , Stopped , &p::stop_playback >,
  166. a_row < Playing , pause , Paused , &p::pause_playback >,
  167. a_row < Playing , open_close , Open , &p::stop_and_open >,
  168. // +---------+-------------+---------+---------------------+----------------------+
  169. a_row < Paused , end_pause , Playing , &p::resume_playback >,
  170. a_row < Paused , stop , Stopped , &p::stop_playback >,
  171. a_row < Paused , open_close , Open , &p::stop_and_open >
  172. // +---------+-------------+---------+---------------------+----------------------+
  173. > {};
  174. // Replaces the default no-transition response.
  175. template <class FSM,class Event>
  176. void no_transition(Event const& , FSM&,int)
  177. {
  178. BOOST_FAIL("no_transition called!");
  179. }
  180. // init counters
  181. template <class Event,class FSM>
  182. void on_entry(Event const&,FSM& fsm)
  183. {
  184. fsm.template get_state<player_::Stopped&>().entry_counter=0;
  185. fsm.template get_state<player_::Stopped&>().exit_counter=0;
  186. fsm.template get_state<player_::Open&>().entry_counter=0;
  187. fsm.template get_state<player_::Open&>().exit_counter=0;
  188. fsm.template get_state<player_::Empty&>().entry_counter=0;
  189. fsm.template get_state<player_::Empty&>().exit_counter=0;
  190. fsm.template get_state<player_::Empty&>().some_dummy_data=3;
  191. fsm.template get_state<player_::Playing&>().entry_counter=0;
  192. fsm.template get_state<player_::Playing&>().exit_counter=0;
  193. fsm.template get_state<player_::Paused&>().entry_counter=0;
  194. fsm.template get_state<player_::Paused&>().exit_counter=0;
  195. }
  196. };
  197. // Pick a back-end
  198. typedef msm::back::state_machine<player_> player;
  199. // static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" };
  200. BOOST_AUTO_TEST_CASE( my_test )
  201. {
  202. player p;
  203. p.start();
  204. BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 1,"Empty entry not called correctly");
  205. p.process_event(open_close());
  206. BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Open should be active"); //Open
  207. BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 1,"Empty exit not called correctly");
  208. BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().entry_counter == 1,"Open entry not called correctly");
  209. // test the serialization
  210. std::ofstream ofs("fsm.txt");
  211. // save fsm to archive (current state is Open)
  212. {
  213. boost::archive::text_oarchive oa(ofs);
  214. // write class instance to archive
  215. oa << p;
  216. }
  217. // reload fsm in state Open
  218. player p2;
  219. {
  220. // create and open an archive for input
  221. std::ifstream ifs("fsm.txt");
  222. boost::archive::text_iarchive ia(ifs);
  223. // read class state from archive
  224. ia >> p2;
  225. }
  226. // we now use p2 as it was loaded
  227. // check that we kept Empty's data value
  228. BOOST_CHECK_MESSAGE(p2.get_state<player_::Empty&>().some_dummy_data == 3,"Empty not deserialized correctly");
  229. BOOST_CHECK_MESSAGE(p2.front_end_data == 4,"Front-end not deserialized correctly");
  230. p.process_event(open_close());
  231. BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Empty should be active"); //Empty
  232. BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().exit_counter == 1,"Open exit not called correctly");
  233. BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 2,"Empty entry not called correctly");
  234. BOOST_CHECK_MESSAGE(p.can_close_drawer_counter == 1,"guard not called correctly");
  235. p.process_event(
  236. cd_detected("louie, louie",DISK_DVD));
  237. BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Empty should be active"); //Empty
  238. BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().exit_counter == 1,"Open exit not called correctly");
  239. BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 2,"Empty entry not called correctly");
  240. p.process_event(
  241. cd_detected("louie, louie",DISK_CD));
  242. BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped
  243. BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 2,"Empty exit not called correctly");
  244. BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 1,"Stopped entry not called correctly");
  245. p.process_event(play());
  246. BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing
  247. BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 1,"Stopped exit not called correctly");
  248. BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 1,"Playing entry not called correctly");
  249. BOOST_CHECK_MESSAGE(p.start_playback_counter == 1,"action not called correctly");
  250. p.process_event(pause());
  251. BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active"); //Paused
  252. BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 1,"Playing exit not called correctly");
  253. BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().entry_counter == 1,"Paused entry not called correctly");
  254. // go back to Playing
  255. p.process_event(end_pause());
  256. BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing
  257. BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().exit_counter == 1,"Paused exit not called correctly");
  258. BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 2,"Playing entry not called correctly");
  259. p.process_event(pause());
  260. BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active"); //Paused
  261. BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 2,"Playing exit not called correctly");
  262. BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().entry_counter == 2,"Paused entry not called correctly");
  263. p.process_event(stop());
  264. BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped
  265. BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().exit_counter == 2,"Paused exit not called correctly");
  266. BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 2,"Stopped entry not called correctly");
  267. p.process_event(stop());
  268. BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped
  269. BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 2,"Stopped exit not called correctly");
  270. BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 3,"Stopped entry not called correctly");
  271. }
  272. }
  273. // eliminate object tracking (even if serialized through a pointer)
  274. // at the risk of a programming error creating duplicate objects.
  275. // this is to get rid of warning because p is not const
  276. BOOST_CLASS_TRACKING(player, boost::serialization::track_never)