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 struct SomeExternalContext
21 {
SomeExternalContextSomeExternalContext22 SomeExternalContext(int b):bla(b){}
23 int bla;
24 };
25
26 namespace
27 {
28 // events
29 struct play {};
30 struct end_pause {};
31 struct stop {};
32 struct pause {};
33 struct open_close {};
34 struct NextSong {};
35 struct PreviousSong {};
36
37 // A "complicated" event type that carries some data.
38 enum DiskTypeEnum
39 {
40 DISK_CD=0,
41 DISK_DVD=1
42 };
43 struct cd_detected
44 {
cd_detected__anon85cdbec20111::cd_detected45 cd_detected(std::string name, DiskTypeEnum diskType)
46 : name(name),
47 disc_type(diskType)
48 {}
49
50 std::string name;
51 DiskTypeEnum disc_type;
52 };
53
54 // front-end: define the FSM structure
55 struct player_ : public msm::front::state_machine_def<player_>
56 {
player___anon85cdbec20111::player_57 player_(SomeExternalContext& context,int someint)
58 :context_(context)
59 {
60 std::cout << "context value:" << context_.bla << " with value:" << someint << std::endl;
61 context.bla = 10;
62 }
63
64 SomeExternalContext& context_;
65
66 // The list of FSM states
67 struct Empty : public msm::front::state<>
68 {
69 int data_;
Empty__anon85cdbec20111::player_::Empty70 Empty():data_(0){}
Empty__anon85cdbec20111::player_::Empty71 Empty(int i):data_(i){}
72 // every (optional) entry/exit methods get the event passed.
73 template <class Event,class FSM>
on_entry__anon85cdbec20111::player_::Empty74 void on_entry(Event const&,FSM& ) {std::cout << "entering: Empty" << std::endl;}
75 template <class Event,class FSM>
on_exit__anon85cdbec20111::player_::Empty76 void on_exit(Event const&,FSM& ) {std::cout << "leaving: Empty" << std::endl;}
77 };
78 struct Open : public msm::front::state<>
79 {
80 int data_;
Open__anon85cdbec20111::player_::Open81 Open():data_(0){}
Open__anon85cdbec20111::player_::Open82 Open(int i):data_(i){}
83
84 template <class Event,class FSM>
on_entry__anon85cdbec20111::player_::Open85 void on_entry(Event const& ,FSM&) {std::cout << "entering: Open" << std::endl;}
86 template <class Event,class FSM>
on_exit__anon85cdbec20111::player_::Open87 void on_exit(Event const&,FSM& ) {std::cout << "leaving: Open" << std::endl;}
88 };
89
90 // sm_ptr still supported but deprecated as functors are a much better way to do the same thing
91 struct Stopped : public msm::front::state<msm::front::default_base_state,msm::front::sm_ptr>
92 {
93 template <class Event,class FSM>
on_entry__anon85cdbec20111::player_::Stopped94 void on_entry(Event const& ,FSM&) {std::cout << "entering: Stopped" << std::endl;}
95 template <class Event,class FSM>
on_exit__anon85cdbec20111::player_::Stopped96 void on_exit(Event const&,FSM& ) {std::cout << "leaving: Stopped" << std::endl;}
set_sm_ptr__anon85cdbec20111::player_::Stopped97 void set_sm_ptr(player_* pl)
98 {
99 m_player=pl;
100 }
101 player_* m_player;
102 };
103
104 struct Playing_ : public msm::front::state_machine_def<Playing_>
105 {
106 // when playing, the CD is loaded and we are in either pause or playing (duh)
107 template <class Event,class FSM>
on_entry__anon85cdbec20111::player_::Playing_108 void on_entry(Event const&,FSM& ) {std::cout << "entering: Playing" << std::endl;}
109 template <class Event,class FSM>
on_exit__anon85cdbec20111::player_::Playing_110 void on_exit(Event const&,FSM& ) {std::cout << "leaving: Playing" << std::endl;}
111
112 // The list of FSM states
113 struct Song1 : public msm::front::state<>
114 {
115 int data_;
Song1__anon85cdbec20111::player_::Playing_::Song1116 Song1():data_(0){}
Song1__anon85cdbec20111::player_::Playing_::Song1117 Song1(int i):data_(i){}
118
119 template <class Event,class FSM>
on_entry__anon85cdbec20111::player_::Playing_::Song1120 void on_entry(Event const&,FSM& ) {std::cout << "starting: First song" << std::endl;}
121 template <class Event,class FSM>
on_exit__anon85cdbec20111::player_::Playing_::Song1122 void on_exit(Event const&,FSM& ) {std::cout << "finishing: First Song" << std::endl;}
123
124 };
125 struct Song2 : public msm::front::state<>
126 {
127 template <class Event,class FSM>
on_entry__anon85cdbec20111::player_::Playing_::Song2128 void on_entry(Event const&,FSM& ) {std::cout << "starting: Second song" << std::endl;}
129 template <class Event,class FSM>
on_exit__anon85cdbec20111::player_::Playing_::Song2130 void on_exit(Event const&,FSM& ) {std::cout << "finishing: Second Song" << std::endl;}
131 };
132 struct Song3 : public msm::front::state<>
133 {
134 template <class Event,class FSM>
on_entry__anon85cdbec20111::player_::Playing_::Song3135 void on_entry(Event const&,FSM& ) {std::cout << "starting: Third song" << std::endl;}
136 template <class Event,class FSM>
on_exit__anon85cdbec20111::player_::Playing_::Song3137 void on_exit(Event const&,FSM& ) {std::cout << "finishing: Third Song" << std::endl;}
138 };
139 // the initial state. Must be defined
140 typedef Song1 initial_state;
141 // transition actions
start_next_song__anon85cdbec20111::player_::Playing_142 void start_next_song(NextSong const&) { std::cout << "Playing::start_next_song\n"; }
start_prev_song__anon85cdbec20111::player_::Playing_143 void start_prev_song(PreviousSong const&) { std::cout << "Playing::start_prev_song\n"; }
144 // guard conditions
145
146 typedef Playing_ pl; // makes transition table cleaner
147 // Transition table for Playing
148 struct transition_table : mpl::vector4<
149 // Start Event Next Action Guard
150 // +---------+-------------+---------+---------------------+----------------------+
151 a_row < Song1 , NextSong , Song2 , &pl::start_next_song >,
152 a_row < Song2 , PreviousSong, Song1 , &pl::start_prev_song >,
153 a_row < Song2 , NextSong , Song3 , &pl::start_next_song >,
154 a_row < Song3 , PreviousSong, Song2 , &pl::start_prev_song >
155 // +---------+-------------+---------+---------------------+----------------------+
156 > {};
157 // Replaces the default no-transition response.
158 template <class FSM,class Event>
no_transition__anon85cdbec20111::player_::Playing_159 void no_transition(Event const& e, FSM&,int state)
160 {
161 std::cout << "no transition from state " << state
162 << " on event " << typeid(e).name() << std::endl;
163 }
164 };
165 // back-end
166 typedef msm::back::state_machine<Playing_> Playing;
167
168 // state not defining any entry or exit
169 struct Paused : public msm::front::state<>
170 {
171 };
172
173 // the initial state of the player SM. Must be defined
174 typedef Empty initial_state;
175
176 // transition actions
start_playback__anon85cdbec20111::player_177 void start_playback(play const&) { std::cout << "player::start_playback\n"; }
open_drawer__anon85cdbec20111::player_178 void open_drawer(open_close const&) { std::cout << "player::open_drawer\n"; }
close_drawer__anon85cdbec20111::player_179 void close_drawer(open_close const&) { std::cout << "player::close_drawer\n"; }
store_cd_info__anon85cdbec20111::player_180 void store_cd_info(cd_detected const&) { std::cout << "player::store_cd_info\n"; }
stop_playback__anon85cdbec20111::player_181 void stop_playback(stop const&) { std::cout << "player::stop_playback\n"; }
pause_playback__anon85cdbec20111::player_182 void pause_playback(pause const&) { std::cout << "player::pause_playback\n"; }
resume_playback__anon85cdbec20111::player_183 void resume_playback(end_pause const&) { std::cout << "player::resume_playback\n"; }
stop_and_open__anon85cdbec20111::player_184 void stop_and_open(open_close const&) { std::cout << "player::stop_and_open\n"; }
stopped_again__anon85cdbec20111::player_185 void stopped_again(stop const&) {std::cout << "player::stopped_again\n";}
186 // guard conditions
good_disk_format__anon85cdbec20111::player_187 bool good_disk_format(cd_detected const& evt)
188 {
189 // to test a guard condition, let's say we understand only CDs, not DVD
190 if (evt.disc_type != DISK_CD)
191 {
192 std::cout << "wrong disk, sorry" << std::endl;
193 return false;
194 }
195 return true;
196 }
197 // used to show a transition conflict. This guard will simply deactivate one transition and thus
198 // solve the conflict
auto_start__anon85cdbec20111::player_199 bool auto_start(cd_detected const&)
200 {
201 return false;
202 }
203
204 typedef player_ p; // makes transition table cleaner
205
206 // Transition table for player
207 struct transition_table : mpl::vector<
208 // Start Event Next Action Guard
209 // +---------+-------------+---------+---------------------+----------------------+
210 a_row < Stopped , play , Playing , &p::start_playback >,
211 a_row < Stopped , open_close , Open , &p::open_drawer >,
212 _row < Stopped , stop , Stopped >,
213 // +---------+-------------+---------+---------------------+----------------------+
214 a_row < Open , open_close , Empty , &p::close_drawer >,
215 // +---------+-------------+---------+---------------------+----------------------+
216 a_row < Empty , open_close , Open , &p::open_drawer >,
217 row < Empty , cd_detected , Stopped , &p::store_cd_info ,&p::good_disk_format >,
218 row < Empty , cd_detected , Playing , &p::store_cd_info ,&p::auto_start >,
219 // +---------+-------------+---------+---------------------+----------------------+
220 a_row < Playing , stop , Stopped , &p::stop_playback >,
221 a_row < Playing , pause , Paused , &p::pause_playback >,
222 a_row < Playing , open_close , Open , &p::stop_and_open >,
223 // +---------+-------------+---------+---------------------+----------------------+
224 a_row < Paused , end_pause , Playing , &p::resume_playback >,
225 a_row < Paused , stop , Stopped , &p::stop_playback >,
226 a_row < Paused , open_close , Open , &p::stop_and_open >
227 // +---------+-------------+---------+---------------------+----------------------+
228 > {};
229 // Replaces the default no-transition response.
230 template <class FSM,class Event>
no_transition__anon85cdbec20111::player_231 void no_transition(Event const& e, FSM&,int state)
232 {
233 std::cout << "no transition from state " << state
234 << " on event " << typeid(e).name() << std::endl;
235 }
236 };
237 // Pick a back-end
238 typedef msm::back::state_machine<player_> player;
239
240 //
241 // Testing utilities.
242 //
243 static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" };
pstate(player const & p)244 void pstate(player const& p)
245 {
246 std::cout << " -> " << state_names[p.current_state()[0]] << std::endl;
247 }
248
test()249 void test()
250 {
251 SomeExternalContext ctx(3);
252 // example 1
253 // create a player with an argument by reference and an int
254 // (go to the constructor front-end)
255 player p1(boost::ref(ctx),5);
256
257 // example 2
258 // create a player with a copy of an Empty state, an argument by reference and an int
259 // (last 2 go to the constructor front-end)
260 player p2(msm::back::states_ << player_::Empty(1),boost::ref(ctx),5);
261 std::cout << "Empty's data should be 1. Is: " << p2.get_state<player_::Empty&>().data_ << std::endl;
262
263 std::cout << "Set a new Empty state" << std::endl;
264 p2.set_states(msm::back::states_ << player_::Empty(5));
265 std::cout << "Empty's data should be 5. Is: " << p2.get_state<player_::Empty&>().data_ << std::endl;
266
267 std::cout << "Set new Empty and Open states" << std::endl;
268 p2.set_states(msm::back::states_ << player_::Empty(7) << player_::Open(2));
269 std::cout << "Empty's data should be 7. Is: " << p2.get_state<player_::Empty&>().data_ << std::endl;
270 std::cout << "Open's data should be 2. Is: " << p2.get_state<player_::Open&>().data_ << std::endl;
271
272 // example 3
273 // create a player with a copy of an Empty state, a copy of a Playing submachine
274 // (which is itself created by a copy of Song1)
275 // an argument by reference and an int
276 // (last 2 go to the constructor front-end)
277 player p(msm::back::states_ << player_::Empty(1)
278 << player_::Playing(msm::back::states_ << player_::Playing_::Song1(8)),
279 boost::ref(ctx),5);
280
281 std::cout << "Song1's data should be 8. Is: "
282 << p.get_state<player_::Playing&>().get_state<player_::Playing_::Song1&>().data_ << std::endl;
283
284
285 }
286 }
287
main()288 int main()
289 {
290 test();
291 return 0;
292 }
293