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 | // back-end |
12 | #include <boost/msm/back11/state_machine.hpp> |
13 | //front-end |
14 | #include <boost/msm/front/state_machine_def.hpp> |
15 | #ifndef BOOST_MSM_NONSTANDALONE_TEST |
16 | #define BOOST_TEST_MODULE back11_anonymous_test |
17 | #endif |
18 | #include <boost/test/unit_test.hpp> |
19 | |
20 | namespace msm = boost::msm; |
21 | namespace mpl = boost::mpl; |
22 | using namespace boost::msm::front; |
23 | |
24 | namespace |
25 | { |
26 | // events |
27 | struct event1 {}; |
28 | |
29 | |
30 | // front-end: define the FSM structure |
31 | struct my_machine_ : public msm::front::state_machine_def<my_machine_> |
32 | { |
33 | unsigned int state2_to_state3_counter; |
34 | unsigned int state3_to_state4_counter; |
35 | unsigned int always_true_counter; |
36 | unsigned int always_false_counter; |
37 | |
38 | my_machine_(): |
39 | state2_to_state3_counter(0), |
40 | state3_to_state4_counter(0), |
41 | always_true_counter(0), |
42 | always_false_counter(0) |
43 | {} |
44 | |
45 | // The list of FSM states |
46 | struct State1 : public msm::front::state<> |
47 | { |
48 | template <class Event,class FSM> |
49 | void on_entry(Event const&,FSM& ) {++entry_counter;} |
50 | template <class Event,class FSM> |
51 | void on_exit(Event const&,FSM& ) {++exit_counter;} |
52 | int entry_counter; |
53 | int exit_counter; |
54 | }; |
55 | struct State2 : public msm::front::state<> |
56 | { |
57 | template <class Event,class FSM> |
58 | void on_entry(Event const&,FSM& ) {++entry_counter;} |
59 | template <class Event,class FSM> |
60 | void on_exit(Event const&,FSM& ) {++exit_counter;} |
61 | int entry_counter; |
62 | int exit_counter; |
63 | }; |
64 | |
65 | struct State3 : public msm::front::state<> |
66 | { |
67 | template <class Event,class FSM> |
68 | void on_entry(Event const&,FSM& ) {++entry_counter;} |
69 | template <class Event,class FSM> |
70 | void on_exit(Event const&,FSM& ) {++exit_counter;} |
71 | int entry_counter; |
72 | int exit_counter; |
73 | }; |
74 | |
75 | struct State4 : public msm::front::state<> |
76 | { |
77 | template <class Event,class FSM> |
78 | void on_entry(Event const&,FSM& ) {++entry_counter;} |
79 | template <class Event,class FSM> |
80 | void on_exit(Event const&,FSM& ) {++exit_counter;} |
81 | int entry_counter; |
82 | int exit_counter; |
83 | }; |
84 | |
85 | // the initial state of the player SM. Must be defined |
86 | typedef State1 initial_state; |
87 | |
88 | // transition actions |
89 | void State2ToState3(none const&) { ++state2_to_state3_counter; } |
90 | void State3ToState4(none const&) { ++state3_to_state4_counter; } |
91 | // guard conditions |
92 | bool always_true(none const& ) |
93 | { |
94 | ++always_true_counter; |
95 | return true; |
96 | } |
97 | bool always_false(none const& ) |
98 | { |
99 | ++always_false_counter; |
100 | return false; |
101 | } |
102 | |
103 | typedef my_machine_ p; // makes transition table cleaner |
104 | |
105 | // Transition table for player |
106 | struct transition_table : boost::fusion::vector< |
107 | // Start Event Next Action Guard |
108 | // +---------+-------------+---------+---------------------+----------------------+ |
109 | _row < State1 , none , State2 >, |
110 | a_row < State2 , none , State3 , &p::State2ToState3 >, |
111 | // +---------+-------------+---------+---------------------+----------------------+ |
112 | row < State3 , none , State4 , &p::State3ToState4 , &p::always_true >, |
113 | g_row < State3 , none , State4 , &p::always_false >, |
114 | _row < State4 , event1 , State1 > |
115 | // +---------+-------------+---------+---------------------+----------------------+ |
116 | > {}; |
117 | // Replaces the default no-transition response. |
118 | template <class FSM,class Event> |
119 | void no_transition(Event const&, FSM&,int) |
120 | { |
121 | BOOST_FAIL("no_transition called!" ); |
122 | } |
123 | // init counters |
124 | template <class Event,class FSM> |
125 | void on_entry(Event const&,FSM& fsm) |
126 | { |
127 | fsm.template get_state<my_machine_::State1&>().entry_counter=0; |
128 | fsm.template get_state<my_machine_::State1&>().exit_counter=0; |
129 | fsm.template get_state<my_machine_::State2&>().entry_counter=0; |
130 | fsm.template get_state<my_machine_::State2&>().exit_counter=0; |
131 | fsm.template get_state<my_machine_::State3&>().entry_counter=0; |
132 | fsm.template get_state<my_machine_::State3&>().exit_counter=0; |
133 | fsm.template get_state<my_machine_::State4&>().entry_counter=0; |
134 | fsm.template get_state<my_machine_::State4&>().exit_counter=0; |
135 | } |
136 | }; |
137 | // Pick a back-end |
138 | typedef msm::back11::state_machine<my_machine_> my_machine; |
139 | |
140 | //static char const* const state_names[] = { "State1", "State2", "State3", "State4" }; |
141 | |
142 | |
143 | BOOST_AUTO_TEST_CASE( back11_anonymous_test ) |
144 | { |
145 | my_machine p; |
146 | |
147 | // needed to start the highest-level SM. This will call on_entry and mark the start of the SM |
148 | // in this case it will also immediately trigger all anonymous transitions |
149 | p.start(); |
150 | BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"State4 should be active" ); //State4 |
151 | BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State1&>().exit_counter == 1,"State1 exit not called correctly" ); |
152 | BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State1&>().entry_counter == 1,"State1 entry not called correctly" ); |
153 | BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State2&>().exit_counter == 1,"State2 exit not called correctly" ); |
154 | BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State2&>().entry_counter == 1,"State2 entry not called correctly" ); |
155 | BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State3&>().exit_counter == 1,"State3 exit not called correctly" ); |
156 | BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State3&>().entry_counter == 1,"State3 entry not called correctly" ); |
157 | BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State4&>().entry_counter == 1,"State4 entry not called correctly" ); |
158 | BOOST_CHECK_MESSAGE(p.always_true_counter == 1,"guard not called correctly" ); |
159 | BOOST_CHECK_MESSAGE(p.always_false_counter == 1,"guard not called correctly" ); |
160 | BOOST_CHECK_MESSAGE(p.state2_to_state3_counter == 1,"action not called correctly" ); |
161 | BOOST_CHECK_MESSAGE(p.state3_to_state4_counter == 1,"action not called correctly" ); |
162 | |
163 | |
164 | // this event will bring us back to the initial state and thus, a new "loop" will be started |
165 | p.process_event(evt: event1()); |
166 | BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"State4 should be active" ); //State4 |
167 | BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State1&>().exit_counter == 2,"State1 exit not called correctly" ); |
168 | BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State1&>().entry_counter == 2,"State1 entry not called correctly" ); |
169 | BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State2&>().exit_counter == 2,"State2 exit not called correctly" ); |
170 | BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State2&>().entry_counter == 2,"State2 entry not called correctly" ); |
171 | BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State3&>().exit_counter == 2,"State3 exit not called correctly" ); |
172 | BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State3&>().entry_counter == 2,"State3 entry not called correctly" ); |
173 | BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State4&>().entry_counter == 2,"State4 entry not called correctly" ); |
174 | BOOST_CHECK_MESSAGE(p.get_state<my_machine_::State4&>().exit_counter == 1,"State4 exit not called correctly" ); |
175 | BOOST_CHECK_MESSAGE(p.always_true_counter == 2,"guard not called correctly" ); |
176 | BOOST_CHECK_MESSAGE(p.always_false_counter == 2,"guard not called correctly" ); |
177 | BOOST_CHECK_MESSAGE(p.state2_to_state3_counter == 2,"action not called correctly" ); |
178 | BOOST_CHECK_MESSAGE(p.state3_to_state4_counter == 2,"action not called correctly" ); |
179 | |
180 | } |
181 | } |
182 | |
183 | |