Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/boost_1_33_1/libs/mpl/example/fsm/player2.cpp @ 12

Last change on this file since 12 was 12, checked in by landauf, 18 years ago

added boost

  • Property svn:executable set to *
File size: 9.9 KB
Line 
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
24namespace std { using ::clock_t; }
25#endif
26
27namespace mpl = boost::mpl;
28using namespace mpl::placeholders;
29
30// A metafunction that returns the Event associated with a transition.
31template <class Transition>
32struct 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.
39template <class Transition>
40struct 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
49template<class Derived>
50class 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.
55template <class Fsm, int initial_state, class Stt, class Event>
56struct 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.
124template <class Fsm, int initial_state, class Stt, class Event>
125const dispatch_table<Fsm, initial_state, Stt, Event>
126dispatch_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.
130template<class Derived>
131class 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
213namespace  // 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
322int main()
323{
324    test();
325    return 0;
326}
Note: See TracBrowser for help on using the repository browser.