]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
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 | } |