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 namespace 25 { 26 // events 27 struct play {}; 28 struct end_pause {}; 29 struct stop {}; 30 struct pause {}; 31 struct open_close {}; 32 33 // A "complicated" event type that carries some data. 34 enum DiskTypeEnum 35 { 36 DISK_CD=0, 37 DISK_DVD=1 38 }; 39 struct cd_detected 40 { cd_detected__anon63d666530111::cd_detected41 cd_detected(std::string name, DiskTypeEnum diskType) 42 : name(name), 43 disc_type(diskType) 44 {} 45 46 std::string name; 47 DiskTypeEnum disc_type; 48 }; 49 struct SomeExternalContext 50 { SomeExternalContext__anon63d666530111::SomeExternalContext51 SomeExternalContext(int b):bla(b){} 52 int bla; 53 }; 54 // front-end: define the FSM structure 55 struct player_ : public msm::front::state_machine_def<player_> 56 { player___anon63d666530111::player_57 player_(SomeExternalContext& context,int someint): 58 context_(context), 59 someint_(someint) 60 {} 61 62 SomeExternalContext& context_; 63 int someint_; 64 65 // The list of FSM states 66 struct Empty : public msm::front::state<> 67 { 68 template <class Event,class FSM> on_entry__anon63d666530111::player_::Empty69 void on_entry(Event const&,FSM& ) {++entry_counter;} 70 template <class Event,class FSM> on_exit__anon63d666530111::player_::Empty71 void on_exit(Event const&,FSM& ) {++exit_counter;} 72 int entry_counter; 73 int exit_counter; 74 }; 75 struct Open : public msm::front::state<> 76 { 77 template <class Event,class FSM> on_entry__anon63d666530111::player_::Open78 void on_entry(Event const&,FSM& ) {++entry_counter;} 79 template <class Event,class FSM> on_exit__anon63d666530111::player_::Open80 void on_exit(Event const&,FSM& ) {++exit_counter;} 81 int entry_counter; 82 int exit_counter; 83 }; 84 85 // sm_ptr still supported but deprecated as functors are a much better way to do the same thing 86 struct Stopped : public msm::front::state<> 87 { 88 template <class Event,class FSM> on_entry__anon63d666530111::player_::Stopped89 void on_entry(Event const&,FSM& ) {++entry_counter;} 90 template <class Event,class FSM> on_exit__anon63d666530111::player_::Stopped91 void on_exit(Event const&,FSM& ) {++exit_counter;} 92 int entry_counter; 93 int exit_counter; 94 }; 95 96 struct Playing : public msm::front::state<> 97 { 98 template <class Event,class FSM> on_entry__anon63d666530111::player_::Playing99 void on_entry(Event const&,FSM& ) {++entry_counter;} 100 template <class Event,class FSM> on_exit__anon63d666530111::player_::Playing101 void on_exit(Event const&,FSM& ) {++exit_counter;} 102 int entry_counter; 103 int exit_counter; 104 }; 105 106 // state not defining any entry or exit 107 struct Paused : public msm::front::state<> 108 { 109 template <class Event,class FSM> on_entry__anon63d666530111::player_::Paused110 void on_entry(Event const&,FSM& ) {++entry_counter;} 111 template <class Event,class FSM> on_exit__anon63d666530111::player_::Paused112 void on_exit(Event const&,FSM& ) {++exit_counter;} 113 int entry_counter; 114 int exit_counter; 115 }; 116 117 // the initial state of the player SM. Must be defined 118 typedef Empty initial_state; 119 120 // transition actions start_playback__anon63d666530111::player_121 void start_playback(play const&) {} open_drawer__anon63d666530111::player_122 void open_drawer(open_close const&) { } store_cd_info__anon63d666530111::player_123 void store_cd_info(cd_detected const&) { } stop_playback__anon63d666530111::player_124 void stop_playback(stop const&) { } pause_playback__anon63d666530111::player_125 void pause_playback(pause const&) { } resume_playback__anon63d666530111::player_126 void resume_playback(end_pause const&) { } stop_and_open__anon63d666530111::player_127 void stop_and_open(open_close const&) { } stopped_again__anon63d666530111::player_128 void stopped_again(stop const&){} 129 // guard conditions good_disk_format__anon63d666530111::player_130 bool good_disk_format(cd_detected const& evt) 131 { 132 // to test a guard condition, let's say we understand only CDs, not DVD 133 if (evt.disc_type != DISK_CD) 134 { 135 return false; 136 } 137 return true; 138 } can_close_drawer__anon63d666530111::player_139 bool can_close_drawer(open_close const&) 140 { 141 return true; 142 } 143 144 typedef player_ p; // makes transition table cleaner 145 146 // Transition table for player 147 struct transition_table : mpl::vector< 148 // Start Event Next Action Guard 149 // +---------+-------------+---------+---------------------+----------------------+ 150 a_row < Stopped , play , Playing , &p::start_playback >, 151 a_row < Stopped , open_close , Open , &p::open_drawer >, 152 _row < Stopped , stop , Stopped >, 153 // +---------+-------------+---------+---------------------+----------------------+ 154 g_row < Open , open_close , Empty , &p::can_close_drawer >, 155 // +---------+-------------+---------+---------------------+----------------------+ 156 a_row < Empty , open_close , Open , &p::open_drawer >, 157 row < Empty , cd_detected , Stopped , &p::store_cd_info ,&p::good_disk_format >, 158 // +---------+-------------+---------+---------------------+----------------------+ 159 a_row < Playing , stop , Stopped , &p::stop_playback >, 160 a_row < Playing , pause , Paused , &p::pause_playback >, 161 a_row < Playing , open_close , Open , &p::stop_and_open >, 162 // +---------+-------------+---------+---------------------+----------------------+ 163 a_row < Paused , end_pause , Playing , &p::resume_playback >, 164 a_row < Paused , stop , Stopped , &p::stop_playback >, 165 a_row < Paused , open_close , Open , &p::stop_and_open > 166 // +---------+-------------+---------+---------------------+----------------------+ 167 > {}; 168 // Replaces the default no-transition response. 169 template <class FSM,class Event> no_transition__anon63d666530111::player_170 void no_transition(Event const&, FSM&,int) 171 { 172 BOOST_FAIL("no_transition called!"); 173 } 174 // init counters 175 template <class Event,class FSM> on_entry__anon63d666530111::player_176 void on_entry(Event const&,FSM& fsm) 177 { 178 fsm.template get_state<player_::Stopped&>().entry_counter=0; 179 fsm.template get_state<player_::Stopped&>().exit_counter=0; 180 fsm.template get_state<player_::Open&>().entry_counter=0; 181 fsm.template get_state<player_::Open&>().exit_counter=0; 182 fsm.template get_state<player_::Empty&>().entry_counter=0; 183 fsm.template get_state<player_::Empty&>().exit_counter=0; 184 fsm.template get_state<player_::Playing&>().entry_counter=0; 185 fsm.template get_state<player_::Playing&>().exit_counter=0; 186 fsm.template get_state<player_::Paused&>().entry_counter=0; 187 fsm.template get_state<player_::Paused&>().exit_counter=0; 188 fsm.context_ = 20; 189 } 190 191 }; 192 // Pick a back-end 193 typedef msm::back::state_machine<player_> player; 194 195 // static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; 196 197 BOOST_AUTO_TEST_CASE(my_test)198 BOOST_AUTO_TEST_CASE( my_test ) 199 { 200 SomeExternalContext ctx(3); 201 player p(boost::ref(ctx),5); 202 BOOST_CHECK_MESSAGE(p.context_.bla == 3,"wrong value passed"); 203 BOOST_CHECK_MESSAGE(p.someint_ == 5,"wrong value passed"); 204 ctx.bla = 10; 205 BOOST_CHECK_MESSAGE(p.context_.bla == 10,"error with reference"); 206 p.start(); 207 BOOST_CHECK_MESSAGE(p.context_.bla == 20,"error with fsm entry behavior"); 208 } 209 } 210 211