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 #ifndef BOOST_MSM_NONSTANDALONE_TEST 17 #define BOOST_TEST_MODULE MyTest 18 #endif 19 #include <boost/test/unit_test.hpp> 20 21 namespace msm = boost::msm; 22 namespace mpl = boost::mpl; 23 24 25 namespace 26 { 27 struct SomeExternalContext 28 { SomeExternalContext__anonf765a3340111::SomeExternalContext29 SomeExternalContext(int b):bla(b){} 30 int bla; 31 }; 32 // events 33 struct play {}; 34 struct end_pause {}; 35 struct stop {}; 36 struct pause {}; 37 struct open_close {}; 38 struct NextSong {}; 39 struct PreviousSong {}; 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__anonf765a3340111::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 { player___anonf765a3340111::player_61 player_(SomeExternalContext& context,int someint) 62 :context_(context) 63 { 64 BOOST_CHECK_MESSAGE(context_.bla == 3,"Wrong context value"); 65 BOOST_CHECK_MESSAGE(someint == 5,"Wrong int value"); 66 context.bla = 10; 67 } 68 69 SomeExternalContext& context_; 70 71 // The list of FSM states 72 struct Empty : public msm::front::state<> 73 { 74 int data_; Empty__anonf765a3340111::player_::Empty75 Empty():data_(0){} Empty__anonf765a3340111::player_::Empty76 Empty(int i):data_(i){} 77 // every (optional) entry/exit methods get the event passed. 78 template <class Event,class FSM> on_entry__anonf765a3340111::player_::Empty79 void on_entry(Event const&,FSM& ) {std::cout << "entering: Empty" << std::endl;} 80 template <class Event,class FSM> on_exit__anonf765a3340111::player_::Empty81 void on_exit(Event const&,FSM& ) {std::cout << "leaving: Empty" << std::endl;} 82 }; 83 struct Open : public msm::front::state<> 84 { 85 int data_; Open__anonf765a3340111::player_::Open86 Open():data_(0){} Open__anonf765a3340111::player_::Open87 Open(int i):data_(i){} 88 89 template <class Event,class FSM> on_entry__anonf765a3340111::player_::Open90 void on_entry(Event const& ,FSM&) {std::cout << "entering: Open" << std::endl;} 91 template <class Event,class FSM> on_exit__anonf765a3340111::player_::Open92 void on_exit(Event const&,FSM& ) {std::cout << "leaving: Open" << std::endl;} 93 }; 94 95 // sm_ptr still supported but deprecated as functors are a much better way to do the same thing 96 struct Stopped : public msm::front::state<msm::front::default_base_state,msm::front::sm_ptr> 97 { 98 template <class Event,class FSM> on_entry__anonf765a3340111::player_::Stopped99 void on_entry(Event const& ,FSM&) {std::cout << "entering: Stopped" << std::endl;} 100 template <class Event,class FSM> on_exit__anonf765a3340111::player_::Stopped101 void on_exit(Event const&,FSM& ) {std::cout << "leaving: Stopped" << std::endl;} set_sm_ptr__anonf765a3340111::player_::Stopped102 void set_sm_ptr(player_* pl) 103 { 104 m_player=pl; 105 } 106 player_* m_player; 107 }; 108 109 struct Playing_ : public msm::front::state_machine_def<Playing_> 110 { 111 // when playing, the CD is loaded and we are in either pause or playing (duh) 112 template <class Event,class FSM> on_entry__anonf765a3340111::player_::Playing_113 void on_entry(Event const&,FSM& ) {std::cout << "entering: Playing" << std::endl;} 114 template <class Event,class FSM> on_exit__anonf765a3340111::player_::Playing_115 void on_exit(Event const&,FSM& ) {std::cout << "leaving: Playing" << std::endl;} 116 117 // The list of FSM states 118 struct Song1 : public msm::front::state<> 119 { 120 int data_; Song1__anonf765a3340111::player_::Playing_::Song1121 Song1():data_(0){} Song1__anonf765a3340111::player_::Playing_::Song1122 Song1(int i):data_(i){} 123 124 template <class Event,class FSM> on_entry__anonf765a3340111::player_::Playing_::Song1125 void on_entry(Event const&,FSM& ) {std::cout << "starting: First song" << std::endl;} 126 template <class Event,class FSM> on_exit__anonf765a3340111::player_::Playing_::Song1127 void on_exit(Event const&,FSM& ) {std::cout << "finishing: First Song" << std::endl;} 128 129 }; 130 struct Song2 : public msm::front::state<> 131 { 132 template <class Event,class FSM> on_entry__anonf765a3340111::player_::Playing_::Song2133 void on_entry(Event const&,FSM& ) {std::cout << "starting: Second song" << std::endl;} 134 template <class Event,class FSM> on_exit__anonf765a3340111::player_::Playing_::Song2135 void on_exit(Event const&,FSM& ) {std::cout << "finishing: Second Song" << std::endl;} 136 }; 137 struct Song3 : public msm::front::state<> 138 { 139 template <class Event,class FSM> on_entry__anonf765a3340111::player_::Playing_::Song3140 void on_entry(Event const&,FSM& ) {std::cout << "starting: Third song" << std::endl;} 141 template <class Event,class FSM> on_exit__anonf765a3340111::player_::Playing_::Song3142 void on_exit(Event const&,FSM& ) {std::cout << "finishing: Third Song" << std::endl;} 143 }; 144 // the initial state. Must be defined 145 typedef Song1 initial_state; 146 // transition actions start_next_song__anonf765a3340111::player_::Playing_147 void start_next_song(NextSong const&) { std::cout << "Playing::start_next_song\n"; } start_prev_song__anonf765a3340111::player_::Playing_148 void start_prev_song(PreviousSong const&) { std::cout << "Playing::start_prev_song\n"; } 149 // guard conditions 150 151 typedef Playing_ pl; // makes transition table cleaner 152 // Transition table for Playing 153 struct transition_table : mpl::vector4< 154 // Start Event Next Action Guard 155 // +---------+-------------+---------+---------------------+----------------------+ 156 a_row < Song1 , NextSong , Song2 , &pl::start_next_song >, 157 a_row < Song2 , PreviousSong, Song1 , &pl::start_prev_song >, 158 a_row < Song2 , NextSong , Song3 , &pl::start_next_song >, 159 a_row < Song3 , PreviousSong, Song2 , &pl::start_prev_song > 160 // +---------+-------------+---------+---------------------+----------------------+ 161 > {}; 162 // Replaces the default no-transition response. 163 template <class FSM,class Event> no_transition__anonf765a3340111::player_::Playing_164 void no_transition(Event const& e, FSM&,int state) 165 { 166 std::cout << "no transition from state " << state 167 << " on event " << typeid(e).name() << std::endl; 168 } 169 }; 170 // back-end 171 typedef msm::back::state_machine<Playing_> Playing; 172 173 // state not defining any entry or exit 174 struct Paused : public msm::front::state<> 175 { 176 }; 177 178 // the initial state of the player SM. Must be defined 179 typedef Empty initial_state; 180 181 // transition actions start_playback__anonf765a3340111::player_182 void start_playback(play const&) { std::cout << "player::start_playback\n"; } open_drawer__anonf765a3340111::player_183 void open_drawer(open_close const&) { std::cout << "player::open_drawer\n"; } close_drawer__anonf765a3340111::player_184 void close_drawer(open_close const&) { std::cout << "player::close_drawer\n"; } store_cd_info__anonf765a3340111::player_185 void store_cd_info(cd_detected const&) { std::cout << "player::store_cd_info\n"; } stop_playback__anonf765a3340111::player_186 void stop_playback(stop const&) { std::cout << "player::stop_playback\n"; } pause_playback__anonf765a3340111::player_187 void pause_playback(pause const&) { std::cout << "player::pause_playback\n"; } resume_playback__anonf765a3340111::player_188 void resume_playback(end_pause const&) { std::cout << "player::resume_playback\n"; } stop_and_open__anonf765a3340111::player_189 void stop_and_open(open_close const&) { std::cout << "player::stop_and_open\n"; } stopped_again__anonf765a3340111::player_190 void stopped_again(stop const&) {std::cout << "player::stopped_again\n";} 191 // guard conditions good_disk_format__anonf765a3340111::player_192 bool good_disk_format(cd_detected const& evt) 193 { 194 // to test a guard condition, let's say we understand only CDs, not DVD 195 if (evt.disc_type != DISK_CD) 196 { 197 std::cout << "wrong disk, sorry" << std::endl; 198 return false; 199 } 200 return true; 201 } 202 // used to show a transition conflict. This guard will simply deactivate one transition and thus 203 // solve the conflict auto_start__anonf765a3340111::player_204 bool auto_start(cd_detected const&) 205 { 206 return false; 207 } 208 209 typedef player_ p; // makes transition table cleaner 210 211 // Transition table for player 212 struct transition_table : mpl::vector< 213 // Start Event Next Action Guard 214 // +---------+-------------+---------+---------------------+----------------------+ 215 a_row < Stopped , play , Playing , &p::start_playback >, 216 a_row < Stopped , open_close , Open , &p::open_drawer >, 217 _row < Stopped , stop , Stopped >, 218 // +---------+-------------+---------+---------------------+----------------------+ 219 a_row < Open , open_close , Empty , &p::close_drawer >, 220 // +---------+-------------+---------+---------------------+----------------------+ 221 a_row < Empty , open_close , Open , &p::open_drawer >, 222 row < Empty , cd_detected , Stopped , &p::store_cd_info ,&p::good_disk_format >, 223 row < Empty , cd_detected , Playing , &p::store_cd_info ,&p::auto_start >, 224 // +---------+-------------+---------+---------------------+----------------------+ 225 a_row < Playing , stop , Stopped , &p::stop_playback >, 226 a_row < Playing , pause , Paused , &p::pause_playback >, 227 a_row < Playing , open_close , Open , &p::stop_and_open >, 228 // +---------+-------------+---------+---------------------+----------------------+ 229 a_row < Paused , end_pause , Playing , &p::resume_playback >, 230 a_row < Paused , stop , Stopped , &p::stop_playback >, 231 a_row < Paused , open_close , Open , &p::stop_and_open > 232 // +---------+-------------+---------+---------------------+----------------------+ 233 > {}; 234 // Replaces the default no-transition response. 235 template <class FSM,class Event> no_transition__anonf765a3340111::player_236 void no_transition(Event const& , FSM&,int) 237 { 238 BOOST_FAIL("no_transition called!"); 239 } 240 }; 241 // Pick a back-end 242 typedef msm::back::state_machine<player_> player; 243 244 BOOST_AUTO_TEST_CASE(my_test)245 BOOST_AUTO_TEST_CASE( my_test ) 246 { 247 SomeExternalContext ctx(3); 248 player p1(boost::ref(ctx),5); 249 BOOST_CHECK_MESSAGE(p1.context_.bla == 10,"Wrong returned context value"); 250 251 ctx.bla = 3; 252 player p2(msm::back::states_ << player_::Empty(1),boost::ref(ctx),5); 253 BOOST_CHECK_MESSAGE(p2.get_state<player_::Empty&>().data_ == 1,"Wrong Empty value"); 254 255 p2.set_states(msm::back::states_ << player_::Empty(5)); 256 BOOST_CHECK_MESSAGE(p2.get_state<player_::Empty&>().data_ == 5,"Wrong Empty value"); 257 258 p2.set_states(msm::back::states_ << player_::Empty(7) << player_::Open(2)); 259 BOOST_CHECK_MESSAGE(p2.get_state<player_::Empty&>().data_ == 7,"Wrong Empty value"); 260 BOOST_CHECK_MESSAGE(p2.get_state<player_::Open&>().data_ == 2,"Wrong Open value"); 261 262 ctx.bla = 3; 263 player p(msm::back::states_ << player_::Empty(1) 264 << player_::Playing(msm::back::states_ << player_::Playing_::Song1(8)), 265 boost::ref(ctx),5); 266 BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().get_state<player_::Playing_::Song1&>().data_ == 8,"Wrong Open value"); 267 } 268 } 269 270