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 <vector>
12 #include <iostream>
13 // back-end
14 #include <boost/msm/back/state_machine.hpp>
15 //front-end
16 #include <boost/msm/front/state_machine_def.hpp>
17 // functors
18 #include <boost/msm/front/functor_row.hpp>
19 #include <boost/msm/front/euml/common.hpp>
20 // for And_ operator
21 #include <boost/msm/front/euml/operator.hpp>
22 // for func_state and func_state_machine
23 #include <boost/msm/front/euml/state_grammar.hpp>
24 
25 using namespace std;
26 namespace msm = boost::msm;
27 namespace mpl = boost::mpl;
28 using namespace msm::front;
29 // for And_ operator
30 using namespace msm::front::euml;
31 
32 namespace  // Concrete FSM implementation
33 {
34     // events
35     struct play {};
36     struct end_pause {};
37     struct stop {};
38     struct pause {};
39     struct open_close {};
40 
41     // A "complicated" event type that carries some data.
42     enum DiskTypeEnum
43     {
44         DISK_CD=0,
45         DISK_DVD=1
46     };
47     struct cd_detected
48     {
cd_detected__anona1607eab0111::cd_detected49         cd_detected(std::string name, DiskTypeEnum diskType)
50             : name(name),
51             disc_type(diskType)
52         {}
53 
54         std::string name;
55         DiskTypeEnum disc_type;
56     };
57 
58     // front-end: define the FSM structure
59     struct player_ : public msm::front::state_machine_def<player_>
60     {
61         // The list of FSM states
62         // entry and exit functors for Empty
63         struct Empty_Entry
64         {
65             template <class Event,class FSM,class STATE>
operator ()__anona1607eab0111::player_::Empty_Entry66             void operator()(Event const&,FSM&,STATE& )
67             {
68                 std::cout << "entering: Empty" << std::endl;
69             }
70         };
71         struct Empty_Exit
72         {
73             template <class Event,class FSM,class STATE>
operator ()__anona1607eab0111::player_::Empty_Exit74             void operator()(Event const&,FSM&,STATE& )
75             {
76                 std::cout << "leaving: Empty" << std::endl;
77             }
78         };
79         // definition of Empty
80         struct Empty_tag {};
81         typedef msm::front::euml::func_state<Empty_tag,Empty_Entry,Empty_Exit> Empty;
82 
83         struct Open_Entry
84         {
85             template <class Event,class FSM,class STATE>
operator ()__anona1607eab0111::player_::Open_Entry86             void operator()(Event const&,FSM&,STATE& )
87             {
88                 std::cout << "entering: Open" << std::endl;
89             }
90         };
91         struct Open_Exit
92         {
93             template <class Event,class FSM,class STATE>
operator ()__anona1607eab0111::player_::Open_Exit94             void operator()(Event const&,FSM&,STATE& )
95             {
96                 std::cout << "leaving: Open" << std::endl;
97             }
98         };
99         struct Open_tag {};
100         typedef msm::front::euml::func_state<Open_tag,Open_Entry,Open_Exit> Open;
101 
102         // states without entry/exit actions (can be declared as functor state, just without functors ;-) )
103         struct Stopped_tag {};
104         typedef msm::front::euml::func_state<Stopped_tag> Stopped;
105 
106         struct Playing_tag {};
107         typedef msm::front::euml::func_state<Playing_tag> Playing;
108 
109         // state not defining any entry or exit (declared as simple state. Equivalent)
110         struct Paused : public msm::front::state<>
111         {
112         };
113 
114         // the initial state of the player SM. Must be defined
115         typedef Empty initial_state;
116 
117         // transition actions
118         // as the functors are generic on events, fsm and source/target state,
119         // you can reuse them in another machine if you wish
120         struct TestFct
121         {
122             template <class EVT,class FSM,class SourceState,class TargetState>
operator ()__anona1607eab0111::player_::TestFct123             void operator()(EVT const&, FSM&,SourceState& ,TargetState& )
124             {
125                 cout << "transition with event:" << typeid(EVT).name() << endl;
126             }
127         };
128         struct start_playback
129         {
130             template <class EVT,class FSM,class SourceState,class TargetState>
operator ()__anona1607eab0111::player_::start_playback131             void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& )
132             {
133                 cout << "player::start_playback" << endl;
134             }
135         };
136         struct open_drawer
137         {
138             template <class EVT,class FSM,class SourceState,class TargetState>
operator ()__anona1607eab0111::player_::open_drawer139             void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& )
140             {
141                 cout << "player::open_drawer" << endl;
142             }
143         };
144         struct close_drawer
145         {
146             template <class EVT,class FSM,class SourceState,class TargetState>
operator ()__anona1607eab0111::player_::close_drawer147             void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& )
148             {
149                 cout << "player::close_drawer" << endl;
150             }
151         };
152         struct store_cd_info
153         {
154             template <class EVT,class FSM,class SourceState,class TargetState>
operator ()__anona1607eab0111::player_::store_cd_info155             void operator()(EVT const&,FSM& fsm ,SourceState& ,TargetState& )
156             {
157                 cout << "player::store_cd_info" << endl;
158                 fsm.process_event(play());
159             }
160         };
161         struct stop_playback
162         {
163             template <class EVT,class FSM,class SourceState,class TargetState>
operator ()__anona1607eab0111::player_::stop_playback164             void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& )
165             {
166                 cout << "player::stop_playback" << endl;
167             }
168         };
169         struct pause_playback
170         {
171             template <class EVT,class FSM,class SourceState,class TargetState>
operator ()__anona1607eab0111::player_::pause_playback172             void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& )
173             {
174                 cout << "player::pause_playback" << endl;
175             }
176         };
177         struct resume_playback
178         {
179             template <class EVT,class FSM,class SourceState,class TargetState>
operator ()__anona1607eab0111::player_::resume_playback180             void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& )
181             {
182                 cout << "player::resume_playback" << endl;
183             }
184         };
185         struct stop_and_open
186         {
187             template <class EVT,class FSM,class SourceState,class TargetState>
operator ()__anona1607eab0111::player_::stop_and_open188             void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& )
189             {
190                 cout << "player::stop_and_open" << endl;
191             }
192         };
193         struct stopped_again
194         {
195             template <class EVT,class FSM,class SourceState,class TargetState>
operator ()__anona1607eab0111::player_::stopped_again196             void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& )
197             {
198                 cout << "player::stopped_again" << endl;
199             }
200         };
201         // guard conditions
202         struct DummyGuard
203         {
204             template <class EVT,class FSM,class SourceState,class TargetState>
operator ()__anona1607eab0111::player_::DummyGuard205             bool operator()(EVT const& evt,FSM& fsm,SourceState& src,TargetState& tgt)
206             {
207                 return true;
208             }
209         };
210         struct good_disk_format
211         {
212             template <class EVT,class FSM,class SourceState,class TargetState>
operator ()__anona1607eab0111::player_::good_disk_format213             bool operator()(EVT const& evt ,FSM&,SourceState& ,TargetState& )
214             {
215                 // to test a guard condition, let's say we understand only CDs, not DVD
216                 if (evt.disc_type != DISK_CD)
217                 {
218                     std::cout << "wrong disk, sorry" << std::endl;
219                     return false;
220                 }
221                 return true;
222             }
223         };
224         struct always_true
225         {
226             template <class EVT,class FSM,class SourceState,class TargetState>
operator ()__anona1607eab0111::player_::always_true227             bool operator()(EVT const& evt ,FSM&,SourceState& ,TargetState& )
228             {
229                 return true;
230             }
231         };
232         // we want to define one row with the classic look.
auto_start__anona1607eab0111::player_233         bool auto_start(cd_detected const& evt)
234         {
235             return false;
236         }
237 
238         typedef player_ p; // makes transition table cleaner
239 
240         // Transition table for player
241         struct transition_table : mpl::vector<
242             //    Start     Event         Next      Action                     Guard
243             //  +---------+-------------+---------+---------------------------+----------------------+
244             Row < Stopped , play        , Playing , ActionSequence_
245                                                      <mpl::vector<
246                                                      TestFct,start_playback> >
247                                                                               , DummyGuard           >,
248             Row < Stopped , open_close  , Open    , open_drawer               , none                 >,
249             Row < Stopped , stop        , Stopped , none                      , none                 >,
250             //  +---------+-------------+---------+---------------------------+----------------------+
251             Row < Open    , open_close  , Empty   , close_drawer              , none                 >,
252             //  +---------+-------------+---------+---------------------------+----------------------+
253             Row < Empty   , open_close  , Open    , open_drawer               , none                 >,
254             Row < Empty   , cd_detected , Stopped , store_cd_info             , And_<good_disk_format,
255                                                                                      always_true>    >,
256             // we here also mix with some "classical row"
257           g_row < Empty   , cd_detected , Playing                             , &p::auto_start       >,
258             //  +---------+-------------+---------+---------------------------+----------------------+
259             Row < Playing , stop        , Stopped , stop_playback             , none                 >,
260             Row < Playing , pause       , Paused  , pause_playback            , none                 >,
261             Row < Playing , open_close  , Open    , stop_and_open             , none                 >,
262             //  +---------+-------------+---------+---------------------------+----------------------+
263             Row < Paused  , end_pause   , Playing , resume_playback           , none                 >,
264             Row < Paused  , stop        , Stopped , stop_playback             , none                 >,
265             Row < Paused  , open_close  , Open    , stop_and_open             , none                 >
266             //  +---------+-------------+---------+---------------------------+----------------------+
267         > {};
268         // Replaces the default no-transition response.
269         template <class FSM,class Event>
no_transition__anona1607eab0111::player_270         void no_transition(Event const& e, FSM&,int state)
271         {
272             std::cout << "no transition from state " << state
273                 << " on event " << typeid(e).name() << std::endl;
274         }
275     };
276     // Pick a back-end
277     typedef msm::back::state_machine<player_> player;
278 
279     //
280     // Testing utilities.
281     //
282     static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" };
pstate(player const & p)283     void pstate(player const& p)
284     {
285         std::cout << " -> " << state_names[p.current_state()[0]] << std::endl;
286     }
287 
test()288     void test()
289     {
290         player p;
291         // needed to start the highest-level SM. This will call on_entry and mark the start of the SM
292         p.start();
293         // go to Open, call on_exit on Empty, then action, then on_entry on Open
294         p.process_event(open_close()); pstate(p);
295         p.process_event(open_close()); pstate(p);
296         // will be rejected, wrong disk type
297         p.process_event(
298             cd_detected("louie, louie",DISK_DVD)); pstate(p);
299         p.process_event(
300             cd_detected("louie, louie",DISK_CD)); pstate(p);
301         // no need to call play() as the previous event does it in its action method
302         //p.process_event(play());
303 
304         // at this point, Play is active
305         p.process_event(pause()); pstate(p);
306         // go back to Playing
307         p.process_event(end_pause());  pstate(p);
308         p.process_event(pause()); pstate(p);
309         p.process_event(stop());  pstate(p);
310         // event leading to the same state
311         // no action method called as it is not present in the transition table
312         p.process_event(stop());  pstate(p);
313     }
314 }
315 
main()316 int main()
317 {
318     test();
319     return 0;
320 }
321