Serialize.cpp 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. #include <iostream>
  2. // back-end
  3. #include <boost/msm/back/state_machine.hpp>
  4. //front-end
  5. #include <boost/msm/front/state_machine_def.hpp>
  6. // include headers that implement a archive in simple text format
  7. #include <boost/archive/text_oarchive.hpp>
  8. #include <boost/archive/text_iarchive.hpp>
  9. #include <boost/serialization/tracking.hpp>
  10. #include <fstream>
  11. namespace msm = boost::msm;
  12. namespace mpl = boost::mpl;
  13. namespace
  14. {
  15. // events
  16. struct play {};
  17. struct end_pause {};
  18. struct stop {};
  19. struct pause {};
  20. struct open_close {};
  21. // A "complicated" event type that carries some data.
  22. enum DiskTypeEnum
  23. {
  24. DISK_CD=0,
  25. DISK_DVD=1
  26. };
  27. struct cd_detected
  28. {
  29. cd_detected(std::string name, DiskTypeEnum diskType)
  30. : name(name),
  31. disc_type(diskType)
  32. {}
  33. std::string name;
  34. DiskTypeEnum disc_type;
  35. };
  36. // front-end: define the FSM structure
  37. struct player_ : public msm::front::state_machine_def<player_>
  38. {
  39. //we might want to serialize some data contained by the front-end
  40. int front_end_data;
  41. player_():front_end_data(0){}
  42. // to achieve this, ask for it
  43. typedef int do_serialize;
  44. // and provide a serialize
  45. template<class Archive>
  46. void serialize(Archive & ar, const unsigned int )
  47. {
  48. ar & front_end_data;
  49. }
  50. // The list of FSM states
  51. struct Empty : public msm::front::state<>
  52. {
  53. // we want Empty to be serialized
  54. typedef int do_serialize;
  55. template<class Archive>
  56. void serialize(Archive & ar, const unsigned int )
  57. {
  58. ar & some_dummy_data;
  59. }
  60. Empty():some_dummy_data(0){}
  61. // every (optional) entry/exit methods get the event passed.
  62. template <class Event,class FSM>
  63. void on_entry(Event const&,FSM& ) {std::cout << "entering: Empty" << std::endl;}
  64. template <class Event,class FSM>
  65. void on_exit(Event const&,FSM& ) {std::cout << "leaving: Empty" << std::endl;}
  66. int some_dummy_data;
  67. };
  68. struct Open : public msm::front::state<>
  69. {
  70. template <class Event,class FSM>
  71. void on_entry(Event const& ,FSM&) {std::cout << "entering: Open" << std::endl;}
  72. template <class Event,class FSM>
  73. void on_exit(Event const&,FSM& ) {std::cout << "leaving: Open" << std::endl;}
  74. };
  75. // sm_ptr still supported but deprecated as functors are a much better way to do the same thing
  76. struct Stopped : public msm::front::state<msm::front::default_base_state,msm::front::sm_ptr>
  77. {
  78. template <class Event,class FSM>
  79. void on_entry(Event const& ,FSM&) {std::cout << "entering: Stopped" << std::endl;}
  80. template <class Event,class FSM>
  81. void on_exit(Event const&,FSM& ) {std::cout << "leaving: Stopped" << std::endl;}
  82. void set_sm_ptr(player_* pl)
  83. {
  84. m_player=pl;
  85. }
  86. player_* m_player;
  87. };
  88. struct Playing : public msm::front::state<>
  89. {
  90. template <class Event,class FSM>
  91. void on_entry(Event const&,FSM& ) {std::cout << "entering: Playing" << std::endl;}
  92. template <class Event,class FSM>
  93. void on_exit(Event const&,FSM& ) {std::cout << "leaving: Playing" << std::endl;}
  94. };
  95. // state not defining any entry or exit
  96. struct Paused : public msm::front::state<>
  97. {
  98. };
  99. // the initial state of the player SM. Must be defined
  100. typedef Empty initial_state;
  101. // transition actions
  102. void start_playback(play const&) { std::cout << "player::start_playback\n"; }
  103. void open_drawer(open_close const&) { std::cout << "player::open_drawer\n"; }
  104. void close_drawer(open_close const&) { std::cout << "player::close_drawer\n"; }
  105. void store_cd_info(cd_detected const&) { std::cout << "player::store_cd_info\n"; }
  106. void stop_playback(stop const&) { std::cout << "player::stop_playback\n"; }
  107. void pause_playback(pause const&) { std::cout << "player::pause_playback\n"; }
  108. void resume_playback(end_pause const&) { std::cout << "player::resume_playback\n"; }
  109. void stop_and_open(open_close const&) { std::cout << "player::stop_and_open\n"; }
  110. void stopped_again(stop const&) {std::cout << "player::stopped_again\n";}
  111. // guard conditions
  112. bool good_disk_format(cd_detected const& evt)
  113. {
  114. // to test a guard condition, let's say we understand only CDs, not DVD
  115. if (evt.disc_type != DISK_CD)
  116. {
  117. std::cout << "wrong disk, sorry" << std::endl;
  118. return false;
  119. }
  120. return true;
  121. }
  122. // used to show a transition conflict. This guard will simply deactivate one transition and thus
  123. // solve the conflict
  124. bool auto_start(cd_detected const&)
  125. {
  126. return false;
  127. }
  128. typedef player_ p; // makes transition table cleaner
  129. // Transition table for player
  130. struct transition_table : mpl::vector<
  131. // Start Event Next Action Guard
  132. // +---------+-------------+---------+---------------------+----------------------+
  133. a_row < Stopped , play , Playing , &p::start_playback >,
  134. a_row < Stopped , open_close , Open , &p::open_drawer >,
  135. _row < Stopped , stop , Stopped >,
  136. // +---------+-------------+---------+---------------------+----------------------+
  137. a_row < Open , open_close , Empty , &p::close_drawer >,
  138. // +---------+-------------+---------+---------------------+----------------------+
  139. a_row < Empty , open_close , Open , &p::open_drawer >,
  140. row < Empty , cd_detected , Stopped , &p::store_cd_info ,&p::good_disk_format >,
  141. row < Empty , cd_detected , Playing , &p::store_cd_info ,&p::auto_start >,
  142. // +---------+-------------+---------+---------------------+----------------------+
  143. a_row < Playing , stop , Stopped , &p::stop_playback >,
  144. a_row < Playing , pause , Paused , &p::pause_playback >,
  145. a_row < Playing , open_close , Open , &p::stop_and_open >,
  146. // +---------+-------------+---------+---------------------+----------------------+
  147. a_row < Paused , end_pause , Playing , &p::resume_playback >,
  148. a_row < Paused , stop , Stopped , &p::stop_playback >,
  149. a_row < Paused , open_close , Open , &p::stop_and_open >
  150. // +---------+-------------+---------+---------------------+----------------------+
  151. > {};
  152. // Replaces the default no-transition response.
  153. template <class FSM,class Event>
  154. void no_transition(Event const& e, FSM&,int state)
  155. {
  156. std::cout << "no transition from state " << state
  157. << " on event " << typeid(e).name() << std::endl;
  158. }
  159. };
  160. // Pick a back-end
  161. typedef msm::back::state_machine<player_> player;
  162. //
  163. // Testing utilities.
  164. //
  165. static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" };
  166. void pstate(player const& p)
  167. {
  168. std::cout << " -> " << state_names[p.current_state()[0]] << std::endl;
  169. }
  170. void test()
  171. {
  172. player p;
  173. // needed to start the highest-level SM. This will call on_entry and mark the start of the SM
  174. p.start();
  175. p.get_state<player_::Empty&>().some_dummy_data=3;
  176. p.front_end_data=4;
  177. // go to Open, call on_exit on Empty, then action, then on_entry on Open
  178. p.process_event(open_close()); pstate(p);
  179. std::ofstream ofs("fsm.txt");
  180. // save fsm to archive (current state is Open)
  181. {
  182. boost::archive::text_oarchive oa(ofs);
  183. // write class instance to archive
  184. oa << p;
  185. }
  186. // reload fsm in state Open
  187. player p2;
  188. {
  189. // create and open an archive for input
  190. std::ifstream ifs("fsm.txt");
  191. boost::archive::text_iarchive ia(ifs);
  192. // read class state from archive
  193. ia >> p2;
  194. }
  195. // we now use p2 as it was loaded
  196. // check that we kept Empty's data value
  197. std::cout << "Empty's data should be 3:" << p2.get_state<player_::Empty&>().some_dummy_data << std::endl;
  198. std::cout << "front-end data should be 4:" << p2.front_end_data << std::endl;
  199. p2.process_event(open_close()); pstate(p2);
  200. // will be rejected, wrong disk type
  201. p2.process_event(
  202. cd_detected("louie, louie",DISK_DVD)); pstate(p2);
  203. p2.process_event(
  204. cd_detected("louie, louie",DISK_CD)); pstate(p2);
  205. p2.process_event(play());
  206. // at this point, Play is active
  207. p2.process_event(pause()); pstate(p2);
  208. // go back to Playing
  209. p2.process_event(end_pause()); pstate(p2);
  210. p2.process_event(pause()); pstate(p2);
  211. p2.process_event(stop()); pstate(p2);
  212. // event leading to the same state
  213. // no action method called as it is not present in the transition table
  214. p2.process_event(stop()); pstate(p2);
  215. }
  216. }
  217. // eliminate object tracking (even if serialized through a pointer)
  218. // at the risk of a programming error creating duplicate objects.
  219. // this is to get rid of warning because p is not const
  220. BOOST_CLASS_TRACKING(player, boost::serialization::track_never)
  221. int main()
  222. {
  223. test();
  224. return 0;
  225. }