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 
dispatchevent_dispatcher45     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>
dispatchdefault_event_dispatcher67     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.
executestate_machine::row98         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>
call_no_transition(int state,Event const & e)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>
process_event(Event const & evt)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:
state_machine()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>
no_transition(int state,Event const & e)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 {
cd_detectedcd_detected183     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 
start_playback(play const &)258   void player::start_playback(play const&){}
open_drawer(open_close const &)259   void player::open_drawer(open_close const&){}
close_drawer(open_close const &)260   void player::close_drawer(open_close const&){}
store_cd_info(cd_detected const &)261   void player::store_cd_info(cd_detected const&){}
stop_playback(stop const &)262   void player::stop_playback(stop const&){}
pause_playback(pause const &)263   void player::pause_playback(pause const&){}
resume_playback(play const &)264   void player::resume_playback(play const&){}
stop_and_open(open_close const &)265   void player::stop_and_open(open_close const&){}
266 
267 
268 
269 
main()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 }
288