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__anond3c7b7760111::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 
59     // The list of FSM states
60     // entry and exit functors for Empty
61     struct Empty_Entry
62     {
63         template <class Event,class FSM,class STATE>
operator ()__anond3c7b7760111::Empty_Entry64         void operator()(Event const&,FSM&,STATE& )
65         {
66             std::cout << "entering: Empty" << std::endl;
67         }
68     };
69     struct Empty_Exit
70     {
71         template <class Event,class FSM,class STATE>
operator ()__anond3c7b7760111::Empty_Exit72         void operator()(Event const&,FSM&,STATE& )
73         {
74             std::cout << "leaving: Empty" << std::endl;
75         }
76     };
77     // definition of Empty
78     struct Empty_tag {};
79     typedef msm::front::euml::func_state<Empty_tag,Empty_Entry,Empty_Exit> Empty;
80 
81     struct Open_Entry
82     {
83         template <class Event,class FSM,class STATE>
operator ()__anond3c7b7760111::Open_Entry84         void operator()(Event const&,FSM&,STATE& )
85         {
86             std::cout << "entering: Open" << std::endl;
87         }
88     };
89     struct Open_Exit
90     {
91         template <class Event,class FSM,class STATE>
operator ()__anond3c7b7760111::Open_Exit92         void operator()(Event const&,FSM&,STATE& )
93         {
94             std::cout << "leaving: Open" << std::endl;
95         }
96     };
97     struct Open_tag {};
98     typedef msm::front::euml::func_state<Open_tag,Open_Entry,Open_Exit> Open;
99 
100     // states without entry/exit actions (can be declared as functor state, just without functors ;-) )
101     struct Stopped_tag {};
102     typedef msm::front::euml::func_state<Stopped_tag> Stopped;
103 
104     struct Playing_tag {};
105     typedef msm::front::euml::func_state<Playing_tag> Playing;
106 
107     // state not defining any entry or exit (declared as simple state. Equivalent)
108     struct Paused_tag {};
109     typedef msm::front::euml::func_state<Paused_tag> Paused;
110 
111     // the initial state of the player SM. Must be defined
112     typedef Empty initial_state;
113 
114     // transition actions
115     // as the functors are generic on events, fsm and source/target state,
116     // you can reuse them in another machine if you wish
117     struct TestFct
118     {
119         template <class EVT,class FSM,class SourceState,class TargetState>
operator ()__anond3c7b7760111::TestFct120         void operator()(EVT const&, FSM&,SourceState& ,TargetState& )
121         {
122             cout << "transition with event:" << typeid(EVT).name() << endl;
123         }
124     };
125     struct start_playback
126     {
127         template <class EVT,class FSM,class SourceState,class TargetState>
operator ()__anond3c7b7760111::start_playback128         void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& )
129         {
130             cout << "player::start_playback" << endl;
131         }
132     };
133     struct open_drawer
134     {
135         template <class EVT,class FSM,class SourceState,class TargetState>
operator ()__anond3c7b7760111::open_drawer136         void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& )
137         {
138             cout << "player::open_drawer" << endl;
139         }
140     };
141     struct close_drawer
142     {
143         template <class EVT,class FSM,class SourceState,class TargetState>
operator ()__anond3c7b7760111::close_drawer144         void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& )
145         {
146             cout << "player::close_drawer" << endl;
147         }
148     };
149     struct store_cd_info
150     {
151         template <class EVT,class FSM,class SourceState,class TargetState>
operator ()__anond3c7b7760111::store_cd_info152         void operator()(EVT const&,FSM& fsm ,SourceState& ,TargetState& )
153         {
154             cout << "player::store_cd_info" << endl;
155             fsm.process_event(play());
156         }
157     };
158     struct stop_playback
159     {
160         template <class EVT,class FSM,class SourceState,class TargetState>
operator ()__anond3c7b7760111::stop_playback161         void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& )
162         {
163             cout << "player::stop_playback" << endl;
164         }
165     };
166     struct pause_playback
167     {
168         template <class EVT,class FSM,class SourceState,class TargetState>
operator ()__anond3c7b7760111::pause_playback169         void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& )
170         {
171             cout << "player::pause_playback" << endl;
172         }
173     };
174     struct resume_playback
175     {
176         template <class EVT,class FSM,class SourceState,class TargetState>
operator ()__anond3c7b7760111::resume_playback177         void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& )
178         {
179             cout << "player::resume_playback" << endl;
180         }
181     };
182     struct stop_and_open
183     {
184         template <class EVT,class FSM,class SourceState,class TargetState>
operator ()__anond3c7b7760111::stop_and_open185         void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& )
186         {
187             cout << "player::stop_and_open" << endl;
188         }
189     };
190     struct stopped_again
191     {
192         template <class EVT,class FSM,class SourceState,class TargetState>
operator ()__anond3c7b7760111::stopped_again193         void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& )
194         {
195             cout << "player::stopped_again" << endl;
196         }
197     };
198     // guard conditions
199     struct DummyGuard
200     {
201         template <class EVT,class FSM,class SourceState,class TargetState>
operator ()__anond3c7b7760111::DummyGuard202         bool operator()(EVT const& evt,FSM& fsm,SourceState& src,TargetState& tgt)
203         {
204             return true;
205         }
206     };
207     struct good_disk_format
208     {
209         template <class EVT,class FSM,class SourceState,class TargetState>
operator ()__anond3c7b7760111::good_disk_format210         bool operator()(EVT const& evt ,FSM&,SourceState& ,TargetState& )
211         {
212             // to test a guard condition, let's say we understand only CDs, not DVD
213             if (evt.disc_type != DISK_CD)
214             {
215                 std::cout << "wrong disk, sorry" << std::endl;
216                 return false;
217             }
218             return true;
219         }
220     };
221     struct always_true
222     {
223         template <class EVT,class FSM,class SourceState,class TargetState>
operator ()__anond3c7b7760111::always_true224         bool operator()(EVT const& evt ,FSM&,SourceState& ,TargetState& )
225         {
226             return true;
227         }
228     };
229 
230     // Transition table for player
231     struct player_transition_table : mpl::vector<
232         //    Start     Event         Next      Action                     Guard
233         //  +---------+-------------+---------+---------------------------+----------------------+
234         Row < Stopped , play        , Playing , ActionSequence_
235                                                  <mpl::vector<
236                                                  TestFct,start_playback> >
237                                                                           , DummyGuard           >,
238         Row < Stopped , open_close  , Open    , open_drawer               , none                 >,
239         Row < Stopped , stop        , Stopped , none                      , none                 >,
240         //  +---------+-------------+---------+---------------------------+----------------------+
241         Row < Open    , open_close  , Empty   , close_drawer              , none                 >,
242         //  +---------+-------------+---------+---------------------------+----------------------+
243         Row < Empty   , open_close  , Open    , open_drawer               , none                 >,
244         Row < Empty   , cd_detected , Stopped , store_cd_info             , And_<good_disk_format,
245                                                                                  always_true>    >,
246         //  +---------+-------------+---------+---------------------------+----------------------+
247         Row < Playing , stop        , Stopped , stop_playback             , none                 >,
248         Row < Playing , pause       , Paused  , pause_playback            , none                 >,
249         Row < Playing , open_close  , Open    , stop_and_open             , none                 >,
250         //  +---------+-------------+---------+---------------------------+----------------------+
251         Row < Paused  , end_pause   , Playing , resume_playback           , none                 >,
252         Row < Paused  , stop        , Stopped , stop_playback             , none                 >,
253         Row < Paused  , open_close  , Open    , stop_and_open             , none                 >
254         //  +---------+-------------+---------+---------------------------+----------------------+
255     > {};
256     // fsm definition
257     struct player_tag {};
258     typedef msm::front::euml::func_state_machine<Playing_tag,
259                                                 // transition table
260                                                 player_transition_table,
261                                                 //Initial state
262                                                 Empty> player_;
263     // Pick a back-end
264     typedef msm::back::state_machine<player_> player;
265 
266     //
267     // Testing utilities.
268     //
269     static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" };
pstate(player const & p)270     void pstate(player const& p)
271     {
272         std::cout << " -> " << state_names[p.current_state()[0]] << std::endl;
273     }
274 
test()275     void test()
276     {
277         player p;
278         // needed to start the highest-level SM. This will call on_entry and mark the start of the SM
279         p.start();
280         // go to Open, call on_exit on Empty, then action, then on_entry on Open
281         p.process_event(open_close()); pstate(p);
282         p.process_event(open_close()); pstate(p);
283         // will be rejected, wrong disk type
284         p.process_event(
285             cd_detected("louie, louie",DISK_DVD)); pstate(p);
286         p.process_event(
287             cd_detected("louie, louie",DISK_CD)); pstate(p);
288         // no need to call play() as the previous event does it in its action method
289         //p.process_event(play());
290 
291         // at this point, Play is active
292         p.process_event(pause()); pstate(p);
293         // go back to Playing
294         p.process_event(end_pause());  pstate(p);
295         p.process_event(pause()); pstate(p);
296         p.process_event(stop());  pstate(p);
297         // event leading to the same state
298         // no action method called as it is not present in the transition table
299         p.process_event(stop());  pstate(p);
300     }
301 }
302 
main()303 int main()
304 {
305     test();
306     return 0;
307 }
308