• 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 <vector>
12 #include <iostream>
13 
14 #include <boost/phoenix/phoenix.hpp>
15 
16 // add phoenix support in eUML
17 #define BOOST_MSM_EUML_PHOENIX_SUPPORT
18 #include <boost/msm/back/state_machine.hpp>
19 #include <boost/msm/front/euml/euml.hpp>
20 
21 
22 using namespace std;
23 using namespace boost::msm::front::euml;
24 namespace msm = boost::msm;
25 using namespace boost::phoenix;
26 
27 // entry/exit/action/guard logging functors
28 #include "logging_functors.h"
29 
30 namespace  // Concrete FSM implementation
31 {
32     // events
33     BOOST_MSM_EUML_EVENT(end_pause)
34     BOOST_MSM_EUML_EVENT(stop)
35     BOOST_MSM_EUML_EVENT(pause)
36     BOOST_MSM_EUML_EVENT(open_close)
37     struct play_event : boost::msm::front::euml::euml_event<play_event>
38     {
39     };
40     play_event play;
41 
42     enum DiskTypeEnum
43     {
44         DISK_CD=0,
45         DISK_DVD=1
46     };
47     // A "complicated" event type that carries some data.
48     struct cd_detected_event : boost::msm::front::euml::euml_event<cd_detected_event>
49     {
cd_detected_event__anon8a7b7d9f0111::cd_detected_event50         cd_detected_event(){}
cd_detected_event__anon8a7b7d9f0111::cd_detected_event51         cd_detected_event(std::string const& name,DiskTypeEnum disk):cd_name(name),cd_type(disk){}
52         std::string cd_name;
53         DiskTypeEnum cd_type;
54     };
55     // define an instance for a nicer transition table
56     cd_detected_event cd_detected;
57 
58     // Concrete FSM implementation
59     // The list of FSM states
60     // state not needing any entry or exit
61     BOOST_MSM_EUML_STATE((),Paused)
62 
63     // states with standard eUML actions
64     BOOST_MSM_EUML_STATE(( Stopped_Entry,Stopped_Exit ),Stopped)
65     BOOST_MSM_EUML_STATE(( Playing_Entry,Playing_Exit ),Playing)
66 
67     // a "standard" msm state
68     struct Empty_impl : public msm::front::state<> , public euml_state<Empty_impl>
69     {
70         // this allows us to add some functions
foo__anon8a7b7d9f0111::Empty_impl71         void foo() {std::cout << "Empty::foo " << std::endl;}
72         // standard entry behavior
73         template <class Event,class FSM>
on_entry__anon8a7b7d9f0111::Empty_impl74         void on_entry(Event const& evt,FSM& fsm)
75         {
76             std::cout << "entering: Empty" << std::endl;
77         }
78         template <class Event,class FSM>
on_exit__anon8a7b7d9f0111::Empty_impl79         void on_exit(Event const& evt,FSM& fsm)
80         {
81             std::cout << "leaving: Empty" << std::endl;
82         }
83     };
84     //instance for use in the transition table
85     Empty_impl const Empty;
86 
87     // entry and exit actions as phoenix functions
88     struct open_entry_impl
89     {
90         typedef void result_type;
operator ()__anon8a7b7d9f0111::open_entry_impl91         void operator()()
92         {
93             cout << "entering: Open" << endl;
94         }
95     };
96     boost::phoenix::function<open_entry_impl> open_entry;
97     struct open_exit_impl
98     {
99         typedef void result_type;
operator ()__anon8a7b7d9f0111::open_exit_impl100         void operator()()
101         {
102             cout << "leaving: Open" << endl;
103         }
104     };
105     boost::phoenix::function<open_exit_impl> open_exit;
106 
107     // a state using phoenix for entry/exit actions
108     BOOST_MSM_EUML_STATE(( open_entry(),open_exit() ),Open)
109 
110     // actions and guards using boost::phoenix
111     struct start_playback_impl
112     {
113         typedef void result_type;
operator ()__anon8a7b7d9f0111::start_playback_impl114         void operator()()
115         {
116             cout << "calling: start_playback" << endl;
117         }
118     };
119     boost::phoenix::function<start_playback_impl> start_playback;
120 
121     // a guard taking the event as argument
122     struct good_disk_format_impl
123     {
124         typedef bool result_type;
125 
126         template <class Event>
operator ()__anon8a7b7d9f0111::good_disk_format_impl127         bool operator()(Event const& evt)
128         {
129             // to test a guard condition, let's say we understand only CDs, not DVD
130             if (evt.cd_type!=DISK_CD)
131             {
132                 std::cout << "wrong disk, sorry" << std::endl;
133                 return false;
134             }
135             std::cout << "good disk" << std::endl;
136             return true;
137         }
138     };
139     boost::phoenix::function<good_disk_format_impl> good_disk_format;
140 
141     // a simple action
142     struct store_cd_info_impl
143     {
144         typedef void result_type;
operator ()__anon8a7b7d9f0111::store_cd_info_impl145         void operator()()
146         {
147             cout << "calling: store_cd_info" << endl;
148         }
149     };
150     boost::phoenix::function<store_cd_info_impl> store_cd_info;
151 
152     // an action taking the fsm as argument and sending it a new event
153     struct process_play_impl
154     {
155         typedef void result_type;
156 
157         template <class Fsm>
operator ()__anon8a7b7d9f0111::process_play_impl158         void operator()(Fsm& fsm)
159         {
160             cout << "queuing a play event" << endl;
161             fsm.process_event(play);
162         }
163     };
164     // it is also possible to use BOOST_PHOENIX_ADAPT_CALLABLE to avoid defining a global variable
165     BOOST_PHOENIX_ADAPT_CALLABLE(process_play, process_play_impl, 1)
166 
167 
168     // transition table. Actions and guards are written as phoenix functions
169     BOOST_MSM_EUML_TRANSITION_TABLE((
170           //an action without arguments
171           Playing   == Stopped  + play        / start_playback()                            ,
172           Playing   == Paused   + end_pause                                                 ,
173           //  +------------------------------------------------------------------------------+
174           Empty     == Open     + open_close                                                ,
175           //  +------------------------------------------------------------------------------+
176           Open      == Empty    + open_close                                                ,
177           Open      == Paused   + open_close                                                ,
178           Open      == Stopped  + open_close                                                ,
179           Open      == Playing  + open_close                                                ,
180           //  +------------------------------------------------------------------------------+
181           Paused    == Playing  + pause                                                     ,
182           //  +------------------------------------------------------------------------------+
183           Stopped   == Playing  + stop                                                      ,
184           Stopped   == Paused   + stop                                                      ,
185           // a guard taking the event as argument
186           // and an action made of a phoenix expression of 2 actions
187           // _event is a placeholder for the current event
188           // _fsm is a placeholder for the current state machine
189           Stopped   == Empty    + cd_detected [good_disk_format(_event)]
190                                               / (store_cd_info(),process_play(_fsm)),
191           Stopped   == Stopped  + stop
192           //  +------------------------------------------------------------------------------+
193          ),transition_table)
194 
195     // create a state machine "on the fly"
196     BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT
197                                         init_ << Empty, // Init State
198                                         no_action, // Entry
199                                         no_action, // Exit
200                                         attributes_ << no_attributes_, // Attributes
201                                         configure_ << no_configure_, // configuration
202                                         Log_No_Transition // no_transition handler
203                                         ),
204                                       player_) //fsm name
205 
206     // or simply, if no no_transition handler needed:
207     //BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT
208     //                                    Empty // Init State
209     //                                 ),player_)
210 
211     // choice of back-end
212     typedef msm::back::state_machine<player_> player;
213 
214     //
215     // Testing utilities.
216     //
217     static char const* const state_names[] = { "Stopped", "Paused", "Open", "Empty", "Playing" };
pstate(player const & p)218     void pstate(player const& p)
219     {
220         std::cout << " -> " << state_names[p.current_state()[0]] << std::endl;
221     }
222 
test()223     void test()
224     {
225         player p;
226         // needed to start the highest-level SM. This will call on_entry and mark the start of the SM
227         p.start();
228         // go to Open, call on_exit on Empty, then action, then on_entry on Open
229         p.process_event(open_close); pstate(p);
230         p.process_event(open_close); pstate(p);
231         // will be rejected, wrong disk type
232         p.process_event(
233             cd_detected_event("louie, louie",DISK_DVD)); pstate(p);
234         p.process_event(
235             cd_detected_event("louie, louie",DISK_CD)); pstate(p);
236         // no need to call play as the previous event does it in its action method
237         //p.process_event(play);
238 
239         // at this point, Play is active
240         p.process_event(pause); pstate(p);
241         // go back to Playing
242         p.process_event(end_pause);  pstate(p);
243         p.process_event(pause); pstate(p);
244         p.process_event(stop);  pstate(p);
245         // event leading to the same state
246         // no action method called as none is defined in the transition table
247         p.process_event(stop);  pstate(p);
248         // test call to no_transition
249         p.process_event(pause); pstate(p);
250     }
251 }
252 
main()253 int main()
254 {
255     test();
256     return 0;
257 }
258 
259