• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  
11  #include <iostream>
12  // back-end
13  #include <boost/msm/back/state_machine.hpp>
14  //front-end
15  #include <boost/msm/front/state_machine_def.hpp>
16  
17  namespace msm = boost::msm;
18  namespace mpl = boost::mpl;
19  
20  namespace
21  {
22      // events
23      struct play {};
24      struct end_pause {};
25      struct stop {};
26      struct pause {};
27      struct open_close {};
28      struct NextSong {};
29      struct PreviousSong {};
30  
31      // A "complicated" event type that carries some data.
32      struct cd_detected
33      {
cd_detected__anon4686cf2c0111::cd_detected34          cd_detected(std::string name)
35              : name(name)
36          {}
37  
38          std::string name;
39      };
40  
41      // front-end: define the FSM structure
42      struct player_ : public msm::front::state_machine_def<player_>
43      {
44          // The list of FSM states
45          struct Empty : public msm::front::state<>
46          {
47              // every (optional) entry/exit methods get the event passed
48              template <class Event,class FSM>
on_entry__anon4686cf2c0111::player_::Empty49              void on_entry(Event const&,FSM& ) {std::cout << "entering: Empty" << std::endl;}
50              template <class Event,class FSM>
on_exit__anon4686cf2c0111::player_::Empty51              void on_exit(Event const&,FSM& ) {std::cout << "leaving: Empty" << std::endl;}
52          };
53          struct Open : public msm::front::state<>
54          {
55              template <class Event,class FSM>
on_entry__anon4686cf2c0111::player_::Open56              void on_entry(Event const&,FSM& ) {std::cout << "entering: Open" << std::endl;}
57              template <class Event,class FSM>
on_exit__anon4686cf2c0111::player_::Open58              void on_exit(Event const&,FSM& ) {std::cout << "leaving: Open" << std::endl;}
59          };
60  
61          struct Stopped : public msm::front::state<>
62          {
63              // when stopped, the CD is loaded
64              template <class Event,class FSM>
on_entry__anon4686cf2c0111::player_::Stopped65              void on_entry(Event const&,FSM& ) {std::cout << "entering: Stopped" << std::endl;}
66              template <class Event,class FSM>
on_exit__anon4686cf2c0111::player_::Stopped67              void on_exit(Event const&,FSM& ) {std::cout << "leaving: Stopped" << std::endl;}
68          };
69  
70          struct Playing_ : public msm::front::state_machine_def<Playing_>
71          {
72              // when playing, the CD is loaded and we are in either pause or playing (duh)
73              template <class Event,class FSM>
on_entry__anon4686cf2c0111::player_::Playing_74              void on_entry(Event const&,FSM& ) {std::cout << "entering: Playing" << std::endl;}
75              template <class Event,class FSM>
on_exit__anon4686cf2c0111::player_::Playing_76              void on_exit(Event const&,FSM& ) {std::cout << "leaving: Playing" << std::endl;}
77  
78              // The list of FSM states
79              struct Song1 : public msm::front::state<>
80              {
81                  template <class Event,class FSM>
on_entry__anon4686cf2c0111::player_::Playing_::Song182                  void on_entry(Event const&,FSM& ) {std::cout << "starting: First song" << std::endl;}
83                  template <class Event,class FSM>
on_exit__anon4686cf2c0111::player_::Playing_::Song184                  void on_exit(Event const&,FSM& ) {std::cout << "finishing: First Song" << std::endl;}
85              };
86              struct Song2 : public msm::front::state<>
87              {
88                  template <class Event,class FSM>
on_entry__anon4686cf2c0111::player_::Playing_::Song289                  void on_entry(Event const&,FSM& ) {std::cout << "starting: Second song" << std::endl;}
90                  template <class Event,class FSM>
on_exit__anon4686cf2c0111::player_::Playing_::Song291                  void on_exit(Event const&,FSM& ) {std::cout << "finishing: Second Song" << std::endl;}
92              };
93              struct Song3 : public msm::front::state<>
94              {
95                  template <class Event,class FSM>
on_entry__anon4686cf2c0111::player_::Playing_::Song396                  void on_entry(Event const&,FSM& ) {std::cout << "starting: Third song" << std::endl;}
97                  template <class Event,class FSM>
on_exit__anon4686cf2c0111::player_::Playing_::Song398                  void on_exit(Event const&,FSM& ) {std::cout << "finishing: Third Song" << std::endl;}
99              };
100              // the initial state. Must be defined
101              typedef Song1 initial_state;
102              // transition actions
start_next_song__anon4686cf2c0111::player_::Playing_103              void start_next_song(NextSong const&)       { std::cout << "Playing::start_next_song\n"; }
start_prev_song__anon4686cf2c0111::player_::Playing_104              void start_prev_song(PreviousSong const&)       { std::cout << "Playing::start_prev_song\n"; }
105              // guard conditions
106  
107              typedef Playing_ pl; // makes transition table cleaner
108              // Transition table for Playing
109              struct transition_table : mpl::vector4<
110                  //      Start     Event         Next      Action               Guard
111                  //    +---------+-------------+---------+---------------------+----------------------+
112                  a_row < Song1   , NextSong    , Song2   , &pl::start_next_song                       >,
113                  a_row < Song2   , PreviousSong, Song1   , &pl::start_prev_song                       >,
114                  a_row < Song2   , NextSong    , Song3   , &pl::start_next_song                       >,
115                  a_row < Song3   , PreviousSong, Song2   , &pl::start_prev_song                       >
116                  //    +---------+-------------+---------+---------------------+----------------------+
117              > {};
118              // Replaces the default no-transition response.
119              template <class FSM,class Event>
no_transition__anon4686cf2c0111::player_::Playing_120              void no_transition(Event const& e, FSM&,int state)
121              {
122                  std::cout << "no transition from state " << state
123                      << " on event " << typeid(e).name() << std::endl;
124              }
125  
126          };
127          // back-end
128          // demonstrates Shallow History: if the state gets activated with end_pause
129          // then it will remember the last active state and reactivate it
130          // also possible: AlwaysHistory, the last active state will always be reactivated
131          // or NoHistory, always restart from the initial state
132          typedef msm::back::state_machine<Playing_,msm::back::ShallowHistory<mpl::vector<end_pause> > > Playing;
133  
134          // state not defining any entry or exit
135          struct Paused : public msm::front::state<>
136          {
137              template <class Event,class FSM>
on_entry__anon4686cf2c0111::player_::Paused138              void on_entry(Event const&,FSM& ) {std::cout << "entering: Paused" << std::endl;}
139              template <class Event,class FSM>
on_exit__anon4686cf2c0111::player_::Paused140              void on_exit(Event const&,FSM& ) {std::cout << "leaving: Paused" << std::endl;}
141          };
142  
143          // the initial state of the player SM. Must be defined
144          typedef Empty initial_state;
145  
146          // transition actions
start_playback__anon4686cf2c0111::player_147          void start_playback(play const&)       { std::cout << "player::start_playback\n"; }
open_drawer__anon4686cf2c0111::player_148          void open_drawer(open_close const&)    { std::cout << "player::open_drawer\n"; }
close_drawer__anon4686cf2c0111::player_149          void close_drawer(open_close const&)   { std::cout << "player::close_drawer\n"; }
store_cd_info__anon4686cf2c0111::player_150          void store_cd_info(cd_detected const& cd) {std::cout << "player::store_cd_info\n";}
stop_playback__anon4686cf2c0111::player_151          void stop_playback(stop const&)        { std::cout << "player::stop_playback\n"; }
pause_playback__anon4686cf2c0111::player_152          void pause_playback(pause const&)      { std::cout << "player::pause_playback\n"; }
resume_playback__anon4686cf2c0111::player_153          void resume_playback(end_pause const&)      { std::cout << "player::resume_playback\n"; }
stop_and_open__anon4686cf2c0111::player_154          void stop_and_open(open_close const&)  { std::cout << "player::stop_and_open\n"; }
stopped_again__anon4686cf2c0111::player_155          void stopped_again(stop const&){std::cout << "player::stopped_again\n";}
156          // guard conditions
157  
158          typedef player_ p; // makes transition table cleaner
159  
160          // Transition table for player
161          struct transition_table : mpl::vector<
162              //      Start     Event         Next      Action               Guard
163              //    +---------+-------------+---------+---------------------+----------------------+
164              a_row < Stopped , play        , Playing , &p::start_playback                         >,
165              a_row < Stopped , open_close  , Open    , &p::open_drawer                            >,
166              a_row < Stopped , stop        , Stopped , &p::stopped_again                          >,
167              //    +---------+-------------+---------+---------------------+----------------------+
168              a_row < Open    , open_close  , Empty   , &p::close_drawer                           >,
169              //    +---------+-------------+---------+---------------------+----------------------+
170              a_row < Empty   , open_close  , Open    , &p::open_drawer                            >,
171              a_row < Empty   , cd_detected , Stopped , &p::store_cd_info                          >,
172              //    +---------+-------------+---------+---------------------+----------------------+
173              a_row < Playing , stop        , Stopped , &p::stop_playback                          >,
174              a_row < Playing , pause       , Paused  , &p::pause_playback                         >,
175              a_row < Playing , open_close  , Open    , &p::stop_and_open                          >,
176              //    +---------+-------------+---------+---------------------+----------------------+
177              a_row < Paused  , end_pause   , Playing , &p::resume_playback                        >,
178              a_row < Paused  , stop        , Stopped , &p::stop_playback                          >,
179              a_row < Paused  , open_close  , Open    , &p::stop_and_open                          >
180              //    +---------+-------------+---------+---------------------+----------------------+
181          > {};
182  
183          // Replaces the default no-transition response.
184          template <class FSM,class Event>
no_transition__anon4686cf2c0111::player_185          void no_transition(Event const& e, FSM&,int state)
186          {
187              std::cout << "no transition from state " << state
188                  << " on event " << typeid(e).name() << std::endl;
189          }
190  
191      };
192      // Pick a back-end
193      typedef msm::back::state_machine<player_> player;
194  
195      //
196      // Testing utilities.
197      //
198      static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" };
199  
pstate(player const & p)200      void pstate(player const& p)
201      {
202          std::cout << " -> " << state_names[p.current_state()[0]] << std::endl;
203      }
204  
test()205      void test()
206      {
207          player p;
208  
209          // needed to start the highest-level SM. This will call on_entry and mark the start of the SM
210          p.start();
211          // go to Open, call on_exit on Empty, then action, then on_entry on Open
212          p.process_event(open_close()); pstate(p);
213          p.process_event(open_close()); pstate(p);
214          p.process_event(cd_detected("louie, louie"));
215          p.process_event(play());
216  
217          // at this point, Play is active
218          // make transition happen inside it. Player has no idea about this event but it's ok.
219          p.process_event(NextSong());pstate(p); //2nd song active
220          p.process_event(NextSong());pstate(p);//3rd song active
221          p.process_event(PreviousSong());pstate(p);//2nd song active
222  
223          p.process_event(pause()); pstate(p);
224          // go back to Playing
225          // as you see, remembers the original state as end_pause is an history trigger
226          p.process_event(end_pause());  pstate(p);
227          p.process_event(pause()); pstate(p);
228          p.process_event(stop());  pstate(p);
229          // event leading to the same state
230          p.process_event(stop());  pstate(p);
231          // play does not trigger shallow history => start back from 1st song
232          p.process_event(play());  pstate(p);
233      }
234  }
235  
main()236  int main()
237  {
238      test();
239      return 0;
240  }
241