Imported Upstream version 1.57.0
[platform/upstream/boost.git] / libs / mpl / example / fsm / player2.cpp
1 //
2 // Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed
3 // under the Boost Software License, Version 1.0. (See accompanying
4 // file LICENSE_1_0.txt or copy at
5 // http://www.boost.org/LICENSE_1_0.txt)
6 //
7 #include "boost/mpl/int.hpp"
8 #include "boost/mpl/fold.hpp"
9 #include "boost/mpl/prior.hpp"
10 #include "boost/mpl/count.hpp"
11 #include "boost/mpl/insert.hpp"
12 #include <boost/mpl/greater.hpp>
13 #include <boost/mpl/for_each.hpp>
14 #include <boost/mpl/filter_view.hpp>
15 #include "boost/mpl/vector/vector20.hpp"
16 #include "boost/assert.hpp"
17 #include <boost/type_traits/is_same.hpp>
18
19 #include <vector>
20 #include <ctime>
21 #include <iostream>
22
23 #if defined(BOOST_DINKUMWARE_STDLIB) && BOOST_DINKUMWARE_STDLIB < 310
24 namespace std { using ::clock_t; }
25 #endif
26
27 namespace mpl = boost::mpl;
28 using namespace mpl::placeholders;
29
30 // A metafunction that returns the Event associated with a transition.
31 template <class Transition>
32 struct transition_event
33 {
34     typedef typename Transition::event type;
35 };
36
37 // A metafunction computing the maximum of a transition's source and
38 // end states.
39 template <class Transition>
40 struct transition_max_state
41 {
42     typedef typename mpl::int_<
43         (Transition::current_state > Transition::next_state)
44             ? Transition::current_state
45             : Transition::next_state
46     > type;
47 };
48
49 template<class Derived>
50 class state_machine;
51
52 // Generates a singleton runtime lookup table that maps current state
53 // to a function that makes the FSM take its transition on the given
54 // Event type.
55 template <class Fsm, int initial_state, class Stt, class Event>
56 struct dispatch_table
57 {
58  private:
59     // This is a table of these function pointers.
60     typedef int (*cell)(Fsm&, int, Event const&);
61
62     // Compute the maximum state value in the Fsm so we know how big
63     // to make the table
64     BOOST_STATIC_CONSTANT(
65         int, max_state = (
66             mpl::fold<Stt
67               , mpl::int_<initial_state>
68               , mpl::if_<
69                     mpl::greater<transition_max_state<_2>,_1>
70                   , transition_max_state<_2>
71                   , _1
72                 >
73             >::type::value
74         )
75     );
76
77     // A function object for use with mpl::for_each that stuffs
78     // transitions into cells.
79     struct init_cell
80     {
81         init_cell(dispatch_table* self_)
82           : self(self_)
83         {}
84         
85         // Cell initializer function object, used with mpl::for_each
86         template <class Transition>
87         void operator()(Transition const&) const
88         {
89             self->entries[Transition::current_state] = &Transition::execute;
90         }
91     
92         dispatch_table* self;
93     };
94     
95  public:
96     // initialize the dispatch table for a given Event and Fsm
97     dispatch_table()
98     {
99         // Initialize cells for no transition
100         for (int i = 0; i <= max_state; ++i)
101         {
102             // VC7.1 seems to need the two-phase assignment.
103             cell call_no_transition = &state_machine<Fsm>::call_no_transition;
104             entries[i] = call_no_transition;
105         }
106
107         // Go back and fill in cells for matching transitions.
108         mpl::for_each<
109             mpl::filter_view<
110                 Stt
111               , boost::is_same<transition_event<_>, Event>
112             >
113         >(init_cell(this));
114     }
115
116     // The singleton instance.
117     static const dispatch_table instance;
118
119  public: // data members
120     cell entries[max_state + 1];
121 };
122
123 // This declares the statically-initialized dispatch_table instance.
124 template <class Fsm, int initial_state, class Stt, class Event>
125 const dispatch_table<Fsm, initial_state, Stt, Event>
126 dispatch_table<Fsm, initial_state, Stt, Event>::instance;
127
128 // CRTP base class for state machines.  Pass the actual FSM class as
129 // the Derived parameter.
130 template<class Derived>
131 class state_machine
132 {
133  public: // Member functions
134     
135     // Main function used by clients of the derived FSM to make
136     // transitions.
137     template<class Event>
138     int process_event(Event const& evt)
139     {
140         typedef typename Derived::transition_table stt;
141         typedef dispatch_table<Derived, Derived::initial_state,stt,Event> table;
142         
143         // Call the action
144         return this->m_state
145             = table::instance.entries[this->m_state](
146                 *static_cast<Derived*>(this), this->m_state, evt);
147     }
148
149     // Getter that returns the current state of the FSM
150     int current_state() const
151     {
152         return this->m_state;
153     }
154
155  private:
156     template <class Fsm, int initial_state, class Stt, class Event>
157     friend class dispatch_table;
158     
159     template <class Event>
160     static int call_no_transition(Derived& fsm, int state, Event const& e)
161     {
162         return fsm.no_transition(state, e);
163     }
164
165     // Default no-transition handler.  Can be replaced in the Derived
166     // FSM class.
167     template <class Event>
168     int no_transition(int state, Event const& e)
169     {
170         BOOST_ASSERT(false);
171         return state;
172     }
173     
174  protected:    // interface for the derived class
175     
176     template<class State>
177     state_machine(State state)                  // Construct with an initial state
178         : m_state(state)
179     {
180     }
181
182     state_machine()
183       : m_state(Derived::initial_state)         // Construct with the default initial_state
184     {
185     }
186
187     // Template used to form rows in the transition table
188     template<
189         int CurrentState
190       , class Event
191       , int NextState
192       , void (Derived::*action)(Event const&)
193     >
194     struct row
195     {
196         BOOST_STATIC_CONSTANT(int, current_state = CurrentState);
197         BOOST_STATIC_CONSTANT(int, next_state = NextState);
198         typedef Event event;
199
200         // Take the transition action and return the next state.
201         static int execute(Derived& fsm, int state, Event const& evt)
202         {
203             BOOST_ASSERT(state == current_state);
204             (fsm.*action)(evt);
205             return next_state;
206         }
207     };
208
209  private: // data members
210     int m_state;
211 };
212
213 namespace  // Concrete FSM implementation
214 {
215   // events
216   struct play {};
217   struct stop {};
218   struct pause {};
219   struct open_close {};
220
221   // A "complicated" event type that carries some data.
222   struct cd_detected
223   {
224       cd_detected(std::string name, std::vector<std::clock_t> durations)
225         : name(name)
226         , track_durations(durations)
227       {}
228         
229       std::string name;
230       std::vector<std::clock_t> track_durations;
231   };
232
233   // Concrete FSM implementation 
234   class player : public state_machine<player>
235   {
236       // The list of FSM states
237       enum states {
238           Empty, Open, Stopped, Playing, Paused
239         , initial_state = Empty
240       };
241
242 #ifdef __MWERKS__
243    public: // Codewarrior bug workaround.  Tested at 0x3202
244 #endif 
245       // transition actions
246       void start_playback(play const&)       { std::cout << "player::start_playback\n"; }
247       void open_drawer(open_close const&)    { std::cout << "player::open_drawer\n"; }
248       void close_drawer(open_close const&)   { std::cout << "player::close_drawer\n"; }
249       void store_cd_info(cd_detected const&) { std::cout << "player::store_cd_info\n"; }
250       void stop_playback(stop const&)        { std::cout << "player::stop_playback\n"; }
251       void pause_playback(pause const&)      { std::cout << "player::pause_playback\n"; }
252       void resume_playback(play const&)      { std::cout << "player::resume_playback\n"; }
253       void stop_and_open(open_close const&)  { std::cout << "player::stop_and_open\n"; }
254 #ifdef __MWERKS__
255    private:
256 #endif 
257       friend class state_machine<player>;
258       typedef player p; // makes transition table cleaner
259
260       // Transition table
261       struct transition_table : mpl::vector11<
262           //    Start     Event         Next      Action
263           //  +---------+-------------+---------+---------------------+
264           row < Stopped , play        , Playing , &p::start_playback  >,
265           row < Stopped , open_close  , Open    , &p::open_drawer     >,
266           //  +---------+-------------+---------+---------------------+
267           row < Open    , open_close  , Empty   , &p::close_drawer    >,
268           //  +---------+-------------+---------+---------------------+
269           row < Empty   , open_close  , Open    , &p::open_drawer     >,
270           row < Empty   , cd_detected , Stopped , &p::store_cd_info   >,
271           //  +---------+-------------+---------+---------------------+
272           row < Playing , stop        , Stopped , &p::stop_playback   >,
273           row < Playing , pause       , Paused  , &p::pause_playback  >,
274           row < Playing , open_close  , Open    , &p::stop_and_open   >,
275           //  +---------+-------------+---------+---------------------+
276           row < Paused  , play        , Playing , &p::resume_playback >,
277           row < Paused  , stop        , Stopped , &p::stop_playback   >,
278           row < Paused  , open_close  , Open    , &p::stop_and_open   >
279           //  +---------+-------------+---------+---------------------+
280       > {};
281
282       // Replaces the default no-transition response.
283       template <class Event>
284       int no_transition(int state, Event const& e)
285       {
286           std::cout << "no transition from state " << state
287                     << " on event " << typeid(e).name() << std::endl;
288           return state;
289       }
290   };
291
292   //
293   // Testing utilities.
294   //
295   static char const* const state_names[] = { "Empty", "Open", "Stopped", "Playing", "Paused" };
296
297   void pstate(player const& p)
298   {
299       std::cout << " -> " << state_names[p.current_state()] << std::endl;
300   }
301   
302   void test()
303   {
304       player p;
305       p.process_event(open_close()); pstate(p);
306       p.process_event(open_close()); pstate(p);
307       p.process_event(
308           cd_detected(
309               "louie, louie"
310             , std::vector<std::clock_t>( /* track lengths */ )
311           )
312       );
313       pstate(p);
314       
315       p.process_event(play());  pstate(p);
316       p.process_event(pause()); pstate(p);
317       p.process_event(play());  pstate(p);
318       p.process_event(stop());  pstate(p);
319   }
320 }
321
322 int main()
323 {
324     test();
325     return 0;
326 }