1 | /* |
---|
2 | |
---|
3 | Copyright David Abrahams 2003-2004 |
---|
4 | Copyright Aleksey Gurtovoy 2003-2004 |
---|
5 | |
---|
6 | Distributed under the Boost Software License, Version 1.0. |
---|
7 | (See accompanying file LICENSE_1_0.txt or copy at |
---|
8 | http://www.boost.org/LICENSE_1_0.txt) |
---|
9 | |
---|
10 | This file was automatically extracted from the source of |
---|
11 | "C++ Template Metaprogramming", by David Abrahams and |
---|
12 | Aleksey Gurtovoy. |
---|
13 | |
---|
14 | It was built successfully with GCC 3.4.2 on Windows using |
---|
15 | the following command: |
---|
16 | |
---|
17 | g++ -I..\..\boost_1_32_0 -o%TEMP%\metaprogram-chapter11-example16.exe example16.cpp |
---|
18 | |
---|
19 | |
---|
20 | */ |
---|
21 | #include <boost/mpl/fold.hpp> |
---|
22 | #include <boost/mpl/filter_view.hpp> |
---|
23 | #include <boost/type_traits/is_same.hpp> |
---|
24 | #include <vector> |
---|
25 | #include <ctime> |
---|
26 | #include <boost/mpl/vector.hpp> |
---|
27 | |
---|
28 | #include <boost/mpl/placeholders.hpp> |
---|
29 | #include <boost/mpl/assert.hpp> |
---|
30 | #include <boost/static_assert.hpp> |
---|
31 | namespace mpl = boost::mpl; |
---|
32 | using namespace mpl::placeholders; |
---|
33 | |
---|
34 | #include <cassert> |
---|
35 | |
---|
36 | template< |
---|
37 | class Transition |
---|
38 | , class Next |
---|
39 | > |
---|
40 | struct event_dispatcher |
---|
41 | { |
---|
42 | typedef typename Transition::fsm_t fsm_t; |
---|
43 | typedef typename Transition::event event; |
---|
44 | |
---|
45 | static int dispatch( |
---|
46 | fsm_t& fsm, int state, event const& e) |
---|
47 | { |
---|
48 | if (state == Transition::current_state) |
---|
49 | { |
---|
50 | Transition::execute(fsm, e); |
---|
51 | return Transition::next_state; |
---|
52 | } |
---|
53 | else // move on to the next node in the chain. |
---|
54 | { |
---|
55 | return Next::dispatch(fsm, state, e); |
---|
56 | } |
---|
57 | } |
---|
58 | }; |
---|
59 | |
---|
60 | |
---|
61 | |
---|
62 | template <class Derived> class state_machine; |
---|
63 | |
---|
64 | struct default_event_dispatcher |
---|
65 | { |
---|
66 | template<class FSM, class Event> |
---|
67 | static int dispatch( |
---|
68 | state_machine<FSM>& m, int state, Event const& e) |
---|
69 | { |
---|
70 | return m.call_no_transition(state, e); |
---|
71 | } |
---|
72 | }; |
---|
73 | |
---|
74 | |
---|
75 | template<class Table, class Event> |
---|
76 | struct generate_dispatcher; |
---|
77 | |
---|
78 | template<class Derived> |
---|
79 | class state_machine |
---|
80 | { |
---|
81 | // ... |
---|
82 | protected: |
---|
83 | template< |
---|
84 | int CurrentState |
---|
85 | , class Event |
---|
86 | , int NextState |
---|
87 | , void (Derived::*action)(Event const&) |
---|
88 | > |
---|
89 | struct row |
---|
90 | { |
---|
91 | // for later use by our metaprogram |
---|
92 | static int const current_state = CurrentState; |
---|
93 | static int const next_state = NextState; |
---|
94 | typedef Event event; |
---|
95 | typedef Derived fsm_t; |
---|
96 | |
---|
97 | // do the transition action. |
---|
98 | static void execute(Derived& fsm, Event const& e) |
---|
99 | { |
---|
100 | (fsm.*action)(e); |
---|
101 | } |
---|
102 | }; |
---|
103 | |
---|
104 | |
---|
105 | friend class default_event_dispatcher; |
---|
106 | |
---|
107 | template <class Event> |
---|
108 | int call_no_transition(int state, Event const& e) |
---|
109 | { |
---|
110 | return static_cast<Derived*>(this) // CRTP downcast |
---|
111 | ->no_transition(state, e); |
---|
112 | } |
---|
113 | // |
---|
114 | public: |
---|
115 | |
---|
116 | template<class Event> |
---|
117 | int process_event(Event const& evt) |
---|
118 | { |
---|
119 | // generate the dispatcher type. |
---|
120 | typedef typename generate_dispatcher< |
---|
121 | typename Derived::transition_table, Event |
---|
122 | >::type dispatcher; |
---|
123 | |
---|
124 | // dispatch the event. |
---|
125 | this->state = dispatcher::dispatch( |
---|
126 | *static_cast<Derived*>(this) // CRTP downcast |
---|
127 | , this->state |
---|
128 | , evt |
---|
129 | ); |
---|
130 | |
---|
131 | // return the new state |
---|
132 | return this->state; |
---|
133 | } |
---|
134 | |
---|
135 | // ... |
---|
136 | protected: |
---|
137 | state_machine() |
---|
138 | : state(Derived::initial_state) |
---|
139 | { |
---|
140 | } |
---|
141 | |
---|
142 | private: |
---|
143 | int state; |
---|
144 | // ... |
---|
145 | |
---|
146 | // ... |
---|
147 | public: |
---|
148 | template <class Event> |
---|
149 | int no_transition(int state, Event const& e) |
---|
150 | { |
---|
151 | assert(false); |
---|
152 | return state; |
---|
153 | } |
---|
154 | // ... |
---|
155 | //// |
---|
156 | }; |
---|
157 | |
---|
158 | |
---|
159 | // get the Event associated with a transition. |
---|
160 | template <class Transition> |
---|
161 | struct transition_event |
---|
162 | { |
---|
163 | typedef typename Transition::event type; |
---|
164 | }; |
---|
165 | |
---|
166 | template<class Table, class Event> |
---|
167 | struct generate_dispatcher |
---|
168 | : mpl::fold< |
---|
169 | mpl::filter_view< // select rows triggered by Event |
---|
170 | Table |
---|
171 | , boost::is_same<Event, transition_event<_1> > |
---|
172 | > |
---|
173 | , default_event_dispatcher |
---|
174 | , event_dispatcher<_2,_1> |
---|
175 | > |
---|
176 | {}; |
---|
177 | |
---|
178 | |
---|
179 | |
---|
180 | struct play {}; |
---|
181 | struct open_close {}; |
---|
182 | struct cd_detected { |
---|
183 | cd_detected(char const*, std::vector<std::clock_t> const&) {} |
---|
184 | }; |
---|
185 | #ifdef __GNUC__ // in which pause seems to have a predefined meaning |
---|
186 | # define pause pause_ |
---|
187 | #endif |
---|
188 | struct pause {}; |
---|
189 | struct stop {}; |
---|
190 | |
---|
191 | |
---|
192 | // concrete FSM implementation |
---|
193 | class player : public state_machine<player> |
---|
194 | { |
---|
195 | private: |
---|
196 | // the list of FSM states |
---|
197 | enum states { |
---|
198 | Empty, Open, Stopped, Playing, Paused |
---|
199 | , initial_state = Empty |
---|
200 | }; |
---|
201 | |
---|
202 | |
---|
203 | #ifdef __MWERKS__ |
---|
204 | public: // Codewarrior bug workaround. Tested at 0x3202 |
---|
205 | #endif |
---|
206 | |
---|
207 | void start_playback(play const&); |
---|
208 | void open_drawer(open_close const&); |
---|
209 | void close_drawer(open_close const&); |
---|
210 | void store_cd_info(cd_detected const&); |
---|
211 | void stop_playback(stop const&); |
---|
212 | void pause_playback(pause const&); |
---|
213 | void resume_playback(play const&); |
---|
214 | void stop_and_open(open_close const&); |
---|
215 | |
---|
216 | |
---|
217 | #ifdef __MWERKS__ |
---|
218 | private: |
---|
219 | #endif |
---|
220 | friend class state_machine<player>; |
---|
221 | typedef player p; // makes transition table cleaner |
---|
222 | |
---|
223 | // transition table |
---|
224 | struct transition_table : mpl::vector11< |
---|
225 | |
---|
226 | // Start Event Next Action |
---|
227 | // +---------+-------------+---------+---------------------+ |
---|
228 | row < Stopped , play , Playing , &p::start_playback >, |
---|
229 | row < Stopped , open_close , Open , &p::open_drawer >, |
---|
230 | // +---------+-------------+---------+---------------------+ |
---|
231 | row < Open , open_close , Empty , &p::close_drawer >, |
---|
232 | // +---------+-------------+---------+---------------------+ |
---|
233 | row < Empty , open_close , Open , &p::open_drawer >, |
---|
234 | row < Empty , cd_detected , Stopped , &p::store_cd_info >, |
---|
235 | // +---------+-------------+---------+---------------------+ |
---|
236 | row < Playing , stop , Stopped , &p::stop_playback >, |
---|
237 | row < Playing , pause , Paused , &p::pause_playback >, |
---|
238 | row < Playing , open_close , Open , &p::stop_and_open >, |
---|
239 | // +---------+-------------+---------+---------------------+ |
---|
240 | row < Paused , play , Playing , &p::resume_playback >, |
---|
241 | row < Paused , stop , Stopped , &p::stop_playback >, |
---|
242 | row < Paused , open_close , Open , &p::stop_and_open > |
---|
243 | // +---------+-------------+---------+---------------------+ |
---|
244 | |
---|
245 | > {}; |
---|
246 | typedef |
---|
247 | |
---|
248 | event_dispatcher< |
---|
249 | row<Stopped, play, Playing, &player::start_playback> |
---|
250 | , event_dispatcher< |
---|
251 | row<Paused, play, Playing, &player::resume_playback> |
---|
252 | , default_event_dispatcher |
---|
253 | > |
---|
254 | > |
---|
255 | dummy; |
---|
256 | }; |
---|
257 | |
---|
258 | void player::start_playback(play const&){} |
---|
259 | void player::open_drawer(open_close const&){} |
---|
260 | void player::close_drawer(open_close const&){} |
---|
261 | void player::store_cd_info(cd_detected const&){} |
---|
262 | void player::stop_playback(stop const&){} |
---|
263 | void player::pause_playback(pause const&){} |
---|
264 | void player::resume_playback(play const&){} |
---|
265 | void player::stop_and_open(open_close const&){} |
---|
266 | |
---|
267 | |
---|
268 | |
---|
269 | |
---|
270 | int main() |
---|
271 | { |
---|
272 | player p; // An instance of the FSM |
---|
273 | |
---|
274 | p.process_event(open_close()); // user opens CD player |
---|
275 | p.process_event(open_close()); // inserts CD and closes |
---|
276 | p.process_event( // CD is detected |
---|
277 | cd_detected( |
---|
278 | "louie, louie" |
---|
279 | , std::vector<std::clock_t>( /* track lengths */ ) |
---|
280 | ) |
---|
281 | ); |
---|
282 | p.process_event(play()); // etc. |
---|
283 | p.process_event(pause()); |
---|
284 | p.process_event(play()); |
---|
285 | p.process_event(stop()); |
---|
286 | return 0; |
---|
287 | } |
---|