• 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 #include <string>
13 #include "boost/mpl/vector/vector30.hpp"
14 
15 #include <boost/msm/back/state_machine.hpp>
16 #include <boost/msm/front/state_machine_def.hpp>
17 #include <boost/msm/back/tools.hpp>
18 
19 using namespace std;
20 namespace msm = boost::msm;
21 
22 namespace  // Concrete FSM implementation
23 {
24     // events
25     struct play {};
26     struct end_pause {};
27     struct stop {};
28     struct pause {};
29     struct open_close {};
30     struct NextSong {};
31     struct PreviousSong {};
32     struct ThreeSec {};
33     struct TenSec {};
34     struct go_sleep {};
35     struct error_found {};
36     struct end_error {};
37 
38     // Flags. Allow information about a property of the current state
39     struct PlayingPaused{};
40     struct CDLoaded {};
41     struct FirstSongPlaying {};
42 
43     // A "complicated" event type that carries some data.
44     struct cd_detected
45     {
cd_detected__anond32e30ee0111::cd_detected46         cd_detected(std::string name)
47             : name(name)
48         {}
49 
50         std::string name;
51     };
52     // an easy visitor
53     struct SomeVisitor
54     {
55         template <class T>
visit_state__anond32e30ee0111::SomeVisitor56         void visit_state(T* astate,int i)
57         {
58             std::cout << "visiting state:" << typeid(*astate).name()
59                 << " with data:" << i << std::endl;
60         }
61     };
62     // overwrite of the base state (not default)
63     struct my_visitable_state
64     {
65         // signature of the accept function
66         typedef msm::back::args<void,SomeVisitor&,int> accept_sig;
67 
68         // we also want polymorphic states
~my_visitable_state__anond32e30ee0111::my_visitable_state69         virtual ~my_visitable_state() {}
70         // default implementation for states who do not need to be visited
accept__anond32e30ee0111::my_visitable_state71         void accept(SomeVisitor&,int) const {}
72     };
73 
74     // Concrete FSM implementation
75     struct player_ : public msm::front::state_machine_def<player_,my_visitable_state>
76     {
77         template <class Event,class FSM>
on_entry__anond32e30ee0111::player_78         void on_entry(Event const&,FSM& ) {std::cout << "starting: player" << std::endl;}
79         // The list of FSM states
80         struct Empty : public msm::front::state<my_visitable_state>
81         {
82             typedef mpl::vector<play> deferred_events;
83             // every (optional) entry/exit methods get the event packed as boost::any. Not useful very often.
84             template <class Event,class FSM>
on_entry__anond32e30ee0111::player_::Empty85             void on_entry(Event const&,FSM& ) {std::cout << "entering: Empty" << std::endl;}
86             template <class Event,class FSM>
on_exit__anond32e30ee0111::player_::Empty87             void on_exit(Event const&,FSM& ) {std::cout << "leaving: Empty" << std::endl;}
accept__anond32e30ee0111::player_::Empty88             void accept(SomeVisitor& vis,int i) const
89             {
90                 vis.visit_state(this,i);
91             }
92         };
93         struct Open : public msm::front::state<my_visitable_state>
94         {
95             typedef mpl::vector1<CDLoaded>      flag_list;
96             typedef mpl::vector<play> deferred_events;
97             template <class Event,class FSM>
on_entry__anond32e30ee0111::player_::Open98             void on_entry(Event const&,FSM& ) {std::cout << "entering: Open" << std::endl;}
99             template <class Event,class FSM>
on_exit__anond32e30ee0111::player_::Open100             void on_exit(Event const&,FSM& ) {std::cout << "leaving: Open" << std::endl;}
accept__anond32e30ee0111::player_::Open101             void accept(SomeVisitor& vis,int i) const
102             {
103                 vis.visit_state(this,i);
104             }
105         };
106         // a state needing a pointer to the containing state machine
107         // and using for this the non-default policy
108         // if policy used, set_sm_ptr is needed
109         struct Stopped : public msm::front::state<my_visitable_state>
110         {
111             // when stopped, the CD is loaded
112             typedef mpl::vector1<CDLoaded>      flag_list;
113             template <class Event,class FSM>
on_entry__anond32e30ee0111::player_::Stopped114             void on_entry(Event const&,FSM& ) {std::cout << "entering: Stopped" << std::endl;}
115             template <class Event,class FSM>
on_exit__anond32e30ee0111::player_::Stopped116             void on_exit(Event const&,FSM& ) {std::cout << "leaving: Stopped" << std::endl;}
117         };
118         // the player state machine contains a state which is himself a state machine
119         // it demonstrates Shallow History: if the state gets activated with end_pause
120         // then it will remember the last active state and reactivate it
121         // also possible: AlwaysHistory, the last active state will always be reactivated
122         // or NoHistory, always restart from the initial state
123         struct Playing_ : public msm::front::state_machine_def<Playing_,my_visitable_state >
124         {
125             // when playing, the CD is loaded and we are in either pause or playing (duh)
126             typedef mpl::vector2<PlayingPaused,CDLoaded>        flag_list;
127             template <class Event,class FSM>
on_entry__anond32e30ee0111::player_::Playing_128             void on_entry(Event const&,FSM& ) {std::cout << "entering: Playing" << std::endl;}
129             template <class Event,class FSM>
on_exit__anond32e30ee0111::player_::Playing_130             void on_exit(Event const&,FSM& ) {std::cout << "leaving: Playing" << std::endl;}
accept__anond32e30ee0111::player_::Playing_131             void accept(SomeVisitor& vis,int i) const
132             {
133                 // note that visiting will recursively visit sub-states
134                 vis.visit_state(this,i);
135             }
136             // The list of FSM states
137             // the Playing state machine contains a state which is himself a state machine
138             // so we have a SM containing a SM containing a SM
139             struct Song1_ : public msm::front::state_machine_def<Song1_,my_visitable_state>
140             {
141                 typedef mpl::vector1<FirstSongPlaying>      flag_list;
142                 template <class Event,class FSM>
on_entry__anond32e30ee0111::player_::Playing_::Song1_143                 void on_entry(Event const&,FSM& ) {std::cout << "starting: First song" << std::endl;}
144                 template <class Event,class FSM>
on_exit__anond32e30ee0111::player_::Playing_::Song1_145                 void on_exit(Event const&,FSM& ) {std::cout << "finishing: First Song" << std::endl;}
accept__anond32e30ee0111::player_::Playing_::Song1_146                 void accept(SomeVisitor& vis,int i) const
147                 {
148                     vis.visit_state(this,i);
149                 }
150                 struct LightOn : public msm::front::state<my_visitable_state>
151                 {
152                     template <class Event,class FSM>
on_entry__anond32e30ee0111::player_::Playing_::Song1_::LightOn153                     void on_entry(Event const&,FSM& ) {std::cout << "starting: LightOn" << std::endl;}
154                     template <class Event,class FSM>
on_exit__anond32e30ee0111::player_::Playing_::Song1_::LightOn155                     void on_exit(Event const&,FSM& ) {std::cout << "finishing: LightOn" << std::endl;}
156                 };
157                 struct LightOff : public msm::front::state<my_visitable_state>
158                 {
159                     template <class Event,class FSM>
on_entry__anond32e30ee0111::player_::Playing_::Song1_::LightOff160                     void on_entry(Event const&,FSM& ) {std::cout << "starting: LightOff" << std::endl;}
161                     template <class Event,class FSM>
on_exit__anond32e30ee0111::player_::Playing_::Song1_::LightOff162                     void on_exit(Event const&,FSM& ) {std::cout << "finishing: LightOff" << std::endl;}
163                 };
164                 // the initial state. Must be defined
165                 typedef LightOn initial_state;
166                 // transition actions
turn_light_off__anond32e30ee0111::player_::Playing_::Song1_167                 void turn_light_off(ThreeSec const&)       { std::cout << "3s off::turn light off\n"; }
168                 // guard conditions
169 
170                 typedef Song1_ s; // makes transition table cleaner
171                 // Transition table for Song1
172                 struct transition_table : mpl::vector1<
173                     //    Start     Event         Next      Action               Guard
174                     //  +---------+-------------+---------+---------------------+----------------------+
175                   a_row < LightOn , ThreeSec    , LightOff, &s::turn_light_off                        >
176                     //  +---------+-------------+---------+---------------------+----------------------+
177                 > {};
178                 // Replaces the default no-transition response.
179                 template <class FSM,class Event>
no_transition__anond32e30ee0111::player_::Playing_::Song1_180                 void no_transition(Event const& e, FSM&,int state)
181                 {
182                     std::cout << "no transition from state " << state
183                         << " on event " << typeid(e).name() << std::endl;
184                 }
185             };
186             typedef msm::back::state_machine<Song1_> Song1;
187 
188             struct Song2 : public msm::front::state<my_visitable_state>
189             {
190                 template <class Event,class FSM>
on_entry__anond32e30ee0111::player_::Playing_::Song2191                 void on_entry(Event const&,FSM& ) {std::cout << "starting: Second song" << std::endl;}
192                 template <class Event,class FSM>
on_exit__anond32e30ee0111::player_::Playing_::Song2193                 void on_exit(Event const&,FSM& ) {std::cout << "finishing: Second Song" << std::endl;}
194             };
195             struct Song3 : public msm::front::state<my_visitable_state>
196             {
197                 template <class Event,class FSM>
on_entry__anond32e30ee0111::player_::Playing_::Song3198                 void on_entry(Event const&,FSM& ) {std::cout << "starting: Third song" << std::endl;}
199                 template <class Event,class FSM>
on_exit__anond32e30ee0111::player_::Playing_::Song3200                 void on_exit(Event const&,FSM& ) {std::cout << "finishing: Third Song" << std::endl;}
201             };
202             // the initial state. Must be defined
203             typedef Song1 initial_state;
204             // transition actions
start_next_song__anond32e30ee0111::player_::Playing_205             void start_next_song(NextSong const&)       { std::cout << "Playing::start_next_song\n"; }
start_prev_song__anond32e30ee0111::player_::Playing_206             void start_prev_song(PreviousSong const&)       { std::cout << "Playing::start_prev_song\n"; }
207             // guard conditions
208 
209             typedef Playing_ pl; // makes transition table cleaner
210             // Transition table for Playing
211             struct transition_table : mpl::vector4<
212                 //    Start     Event         Next      Action               Guard
213                 //  +---------+-------------+---------+---------------------+----------------------+
214                 a_row < Song1   , NextSong    , Song2   , &pl::start_next_song                      >,
215                 a_row < Song2   , PreviousSong, Song1   , &pl::start_prev_song                      >,
216                 a_row < Song2   , NextSong    , Song3   , &pl::start_next_song                      >,
217                 a_row < Song3   , PreviousSong, Song2   , &pl::start_prev_song                      >
218                 //  +---------+-------------+---------+---------------------+----------------------+
219             > {};
220             // Replaces the default no-transition response.
221             template <class FSM,class Event>
no_transition__anond32e30ee0111::player_::Playing_222             void no_transition(Event const& e, FSM&,int state)
223             {
224                 std::cout << "no transition from state " << state
225                     << " on event " << typeid(e).name() << std::endl;
226             }
227         };
228         typedef msm::back::state_machine<Playing_,msm::back::ShallowHistory<mpl::vector<end_pause> > > Playing;
229 
230         // the player state machine contains a state which is himself a state machine (2 of them, Playing and Paused)
231         struct Paused_ : public msm::front::state_machine_def<Paused_,my_visitable_state>
232         {
233             typedef mpl::vector2<PlayingPaused,CDLoaded>    flag_list;
234             template <class Event,class FSM>
on_entry__anond32e30ee0111::player_::Paused_235             void on_entry(Event const&,FSM& ) {std::cout << "entering: Paused" << std::endl;}
236             template <class Event,class FSM>
on_exit__anond32e30ee0111::player_::Paused_237             void on_exit(Event const&,FSM& ) {std::cout << "leaving: Paused" << std::endl;}
238 
239             // The list of FSM states
240             struct StartBlinking : public msm::front::state<my_visitable_state>
241             {
242                 template <class Event,class FSM>
on_entry__anond32e30ee0111::player_::Paused_::StartBlinking243                 void on_entry(Event const&,FSM& ) {std::cout << "starting: StartBlinking" << std::endl;}
244                 template <class Event,class FSM>
on_exit__anond32e30ee0111::player_::Paused_::StartBlinking245                 void on_exit(Event const&,FSM& ) {std::cout << "finishing: StartBlinking" << std::endl;}
246             };
247             struct StopBlinking : public msm::front::state<my_visitable_state>
248             {
249                 template <class Event,class FSM>
on_entry__anond32e30ee0111::player_::Paused_::StopBlinking250                 void on_entry(Event const&,FSM& ) {std::cout << "starting: StopBlinking" << std::endl;}
251                 template <class Event,class FSM>
on_exit__anond32e30ee0111::player_::Paused_::StopBlinking252                 void on_exit(Event const&,FSM& ) {std::cout << "finishing: StopBlinking" << std::endl;}
253             };
254             // the initial state. Must be defined
255             typedef StartBlinking initial_state;
256             // transition actions
start_blinking__anond32e30ee0111::player_::Paused_257             void start_blinking(TenSec const&)       { std::cout << "Paused::start_blinking\n"; }
stop_blinking__anond32e30ee0111::player_::Paused_258             void stop_blinking(TenSec const&)       { std::cout << "Paused::stop_blinking\n"; }
259             // guard conditions
260 
261             typedef Paused_ pa; // makes transition table cleaner
262             // Transition table
263             struct transition_table : mpl::vector2<
264                 //    Start          Event         Next           Action                Guard
265                 //  +---------------+-------------+--------------+---------------------+----------------------+
266                 a_row < StartBlinking , TenSec      , StopBlinking  , &pa::stop_blinking                        >,
267                 a_row < StopBlinking  , TenSec      , StartBlinking , &pa::start_blinking                       >
268                 //  +---------------+-------------+---------------+--------------------+----------------------+
269             > {};
270             // Replaces the default no-transition response.
271             template <class FSM,class Event>
no_transition__anond32e30ee0111::player_::Paused_272             void no_transition(Event const& e, FSM&,int state)
273             {
274                 std::cout << "no transition from state " << state
275                     << " on event " << typeid(e).name() << std::endl;
276             }
277         };
278         typedef msm::back::state_machine<Paused_> Paused;
279 
280         struct SleepMode : public msm::front::state<my_visitable_state>
281         {
282         }; // dummy state just to test the automatic id generation
283 
284         struct AllOk : public msm::front::state<my_visitable_state>
285         {
286             template <class Event,class FSM>
on_entry__anond32e30ee0111::player_::AllOk287             void on_entry(Event const&,FSM& ) {std::cout << "starting: AllOk" << std::endl;}
288             template <class Event,class FSM>
on_exit__anond32e30ee0111::player_::AllOk289             void on_exit(Event const&,FSM& ) {std::cout << "finishing: AllOk" << std::endl;}
290         };
291         struct ErrorMode : //public terminate_state<>
292             public msm::front::interrupt_state<end_error,my_visitable_state>
293         {
294             template <class Event,class FSM>
on_entry__anond32e30ee0111::player_::ErrorMode295             void on_entry(Event const&,FSM& ) {std::cout << "starting: ErrorMode" << std::endl;}
296             template <class Event,class FSM>
on_exit__anond32e30ee0111::player_::ErrorMode297             void on_exit(Event const&,FSM& ) {std::cout << "finishing: ErrorMode" << std::endl;}
298         };
299 
300         // the initial state of the player SM. Must be defined
301         typedef mpl::vector<Empty,AllOk> initial_state;
302         //typedef Empty initial_state; // this is to have only one active state
303 
304         // transition actions
start_playback__anond32e30ee0111::player_305         void start_playback(play const&)       { std::cout << "player::start_playback\n"; }
open_drawer__anond32e30ee0111::player_306         void open_drawer(open_close const&)    { std::cout << "player::open_drawer\n"; }
close_drawer__anond32e30ee0111::player_307         void close_drawer(open_close const&)   { std::cout << "player::close_drawer\n"; }
store_cd_info__anond32e30ee0111::player_308         void store_cd_info(cd_detected const&)
309         {
310             std::cout << "player::store_cd_info\n";
311             // generate another event to test the queue
312             //process_event(play());
313         }
stop_playback__anond32e30ee0111::player_314         void stop_playback(stop const&)        { std::cout << "player::stop_playback\n"; }
pause_playback__anond32e30ee0111::player_315         void pause_playback(pause const&)      { std::cout << "player::pause_playback\n"; }
resume_playback__anond32e30ee0111::player_316         void resume_playback(end_pause const&)      { std::cout << "player::resume_playback\n"; }
stop_and_open__anond32e30ee0111::player_317         void stop_and_open(open_close const&)  { std::cout << "player::stop_and_open\n"; }
stopped_again__anond32e30ee0111::player_318         void stopped_again(stop const&){std::cout << "player::stopped_again\n";}
start_sleep__anond32e30ee0111::player_319         void start_sleep(go_sleep const&)  {  }
report_error__anond32e30ee0111::player_320         void report_error(error_found const&) {std::cout << "player::report_error\n";}
report_end_error__anond32e30ee0111::player_321         void report_end_error(end_error const&) {std::cout << "player::report_end_error\n";}
322         // guard conditions
323 
324 
325         typedef player_ p; // makes transition table cleaner
326 
327         // Transition table for player
328         struct transition_table : mpl::vector<
329             //    Start     Event         Next      Action               Guard
330             //  +---------+-------------+---------+---------------------+----------------------+
331           a_row < Stopped , play        , Playing , &p::start_playback                        >,
332           a_row < Stopped , open_close  , Open    , &p::open_drawer                           >,
333           a_row < Stopped , stop        , Stopped , &p::stopped_again                         >,
334             //  +---------+-------------+---------+---------------------+----------------------+
335           a_row < Open    , open_close  , Empty   , &p::close_drawer                          >,
336             //  +---------+-------------+---------+---------------------+----------------------+
337           a_row < Empty   , open_close  , Open    , &p::open_drawer                           >,
338           a_row < Empty   , cd_detected , Stopped , &p::store_cd_info                         >,
339             //  +---------+-------------+---------+---------------------+----------------------+
340           a_row < Playing , stop        , Stopped , &p::stop_playback                         >,
341           a_row < Playing , pause       , Paused  , &p::pause_playback                        >,
342           a_row < Playing , open_close  , Open    , &p::stop_and_open                         >,
343             //  +---------+-------------+---------+---------------------+----------------------+
344           a_row < Paused  , end_pause   , Playing , &p::resume_playback                       >,
345           a_row < Paused  , stop        , Stopped , &p::stop_playback                         >,
346           a_row < Paused  , open_close  , Open    , &p::stop_and_open                         >,
347           a_row < Paused  , go_sleep    ,SleepMode, &p::start_sleep                           >,
348             //  +---------+-------------+---------+---------------------+----------------------+
349           a_row < AllOk   , error_found ,ErrorMode, &p::report_error                          >,
350           a_row <ErrorMode,end_error    ,AllOk    , &p::report_end_error                      >
351             //  +---------+-------------+---------+---------------------+----------------------+
352         > {};
353 
354         // Replaces the default no-transition response.
355         template <class FSM,class Event>
no_transition__anond32e30ee0111::player_356         void no_transition(Event const& e, FSM&,int state)
357         {
358             std::cout << "no transition from state " << state
359                 << " on event " << typeid(e).name() << std::endl;
360         }
361     };
362 
363     // back-end
364     typedef msm::back::state_machine<player_> player;
365 
366     //
367     // Testing utilities.
368     //
369 
pstate(player const & p)370     void pstate(player const& p)
371     {
372         static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused","AllOk","ErrorMode","SleepMode" };
373         for (unsigned int i=0;i<player::nr_regions::value;++i)
374         {
375             std::cout << " -> " << state_names[p.current_state()[i]] << std::endl;
376         }
377     }
test()378     void test()
379     {
380         player p;
381         // needed to start the highest-level SM. This will call on_entry and mark the start of the SM
382         p.start();
383         std::cout << "CDLoaded active:" << std::boolalpha << p.is_flag_active<CDLoaded>() << std::endl; //=> false (no CD yet)
384 
385         // test deferred event
386         // deferred in Empty and Open, will be handled only after event cd_detected
387         p.process_event(play());
388 
389         // go to Open, call on_exit on Empty, then action, then on_entry on Open
390         p.process_event(open_close()); pstate(p);
391         // visiting Paused and AllOk, but only Paused cares
392         SomeVisitor vis;
393         p.visit_current_states(boost::ref(vis),1);
394         p.process_event(open_close()); pstate(p);
395         // visiting Empty and AllOk, but only Empty cares
396         p.visit_current_states(boost::ref(vis),2);
397 
398 
399         p.process_event(cd_detected("louie, louie"));
400         // no need to call play() as the previous event does it in its action method
401         //p.process_event(play());
402         // at this point, Play is active, along FirstSong and LightOn
403         pstate(p);
404         // visiting Playing+Song1 and AllOk, but only Playing+Song1 care
405         p.visit_current_states(boost::ref(vis),3);
406 
407         std::cout << "PlayingPaused active:" << std::boolalpha << p.is_flag_active<PlayingPaused>() << std::endl;//=> true
408         // call on_exit on LightOn,FirstSong,Play like stated in the UML spec.
409         // and of course on_entry on Paused and StartBlinking
410         p.process_event(pause()); pstate(p);
411         std::cout << "PlayingPaused active:" << std::boolalpha << p.is_flag_active<PlayingPaused>() << std::endl;//=> true
412         // forward events to Paused
413         p.process_event(TenSec());
414         p.process_event(TenSec());
415         // go back to Playing
416         p.process_event(end_pause());  pstate(p);
417         std::cout << "FirstSong active:" << std::boolalpha << p.is_flag_active<FirstSongPlaying>() << std::endl; //=> true
418         p.process_event(ThreeSec());  pstate(p);
419         p.process_event(NextSong());pstate(p);
420         // We are now in second song, Flag inactive
421         std::cout << "FirstSong active:" << std::boolalpha << p.is_flag_active<FirstSongPlaying>() << std::endl;//=> false
422         // visiting Playing+Song2 and AllOk, but only Playing cares
423         p.visit_current_states(boost::ref(vis),4);
424 
425         p.process_event(NextSong());pstate(p);
426         // 2nd song active
427         p.process_event(PreviousSong());pstate(p);
428         // Pause
429         p.process_event(pause()); pstate(p);
430         // go back to Playing
431         // but end_pause is an event activating the History
432         // => keep the last active State (SecondSong)
433         p.process_event(end_pause());  pstate(p);
434         // test of an event from a state to itself. According to UML spec, call again exit/entry from Stopped
435         p.process_event(stop());  pstate(p);
436         p.process_event(stop());  pstate(p);
437         std::cout << "CDLoaded active:" << std::boolalpha << p.is_flag_active<CDLoaded>() << std::endl;//=> true
438         std::cout << "PlayingPaused active:" << std::boolalpha << p.is_flag_active<PlayingPaused>() << std::endl;//=> false
439         std::cout << "FirstSong active:" << std::boolalpha << p.is_flag_active<FirstSongPlaying>() << std::endl;//=> false
440         std::cout << "CDLoaded active with AND:" << std::boolalpha << p.is_flag_active<CDLoaded,player::Flag_AND>() << std::endl;//=> false
441         std::cout << "CDLoaded active with OR:" << std::boolalpha << p.is_flag_active<CDLoaded,player::Flag_OR>() << std::endl;//=> true
442 
443         // go back to Playing
444         // but play is not leading to Shallow History => do not remember the last active State (SecondSong)
445         // and activate again FirstSong and LightOn
446         p.process_event(play());  pstate(p);
447         p.process_event(error_found());  pstate(p);
448 
449         // try generating more events
450         std::cout << "Trying to generate another event" << std::endl; // will not work, fsm is terminated or interrupted
451         p.process_event(NextSong());pstate(p);
452 
453         std::cout << "Trying to end the error" << std::endl; // will work only if ErrorMode is interrupt state
454         p.process_event(end_error());pstate(p);
455         std::cout << "Trying to generate another event" << std::endl; // will work only if ErrorMode is interrupt state
456         p.process_event(NextSong());pstate(p);
457 
458         std::cout << "Simulate error. Event play is not valid" << std::endl;
459         p.process_event(play()); pstate(p);
460     }
461 }
462 
main()463 int main()
464 {
465     test();
466     return 0;
467 }
468 
469 
470