summaryrefslogtreecommitdiffstats
path: root/src/boost/libs/mpl/example/fsm/player2.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/boost/libs/mpl/example/fsm/player2.cpp')
-rw-r--r--src/boost/libs/mpl/example/fsm/player2.cpp326
1 files changed, 326 insertions, 0 deletions
diff --git a/src/boost/libs/mpl/example/fsm/player2.cpp b/src/boost/libs/mpl/example/fsm/player2.cpp
new file mode 100644
index 000000000..8704ef5dc
--- /dev/null
+++ b/src/boost/libs/mpl/example/fsm/player2.cpp
@@ -0,0 +1,326 @@
+//
+// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed
+// under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+//
+#include "boost/mpl/int.hpp"
+#include "boost/mpl/fold.hpp"
+#include "boost/mpl/prior.hpp"
+#include "boost/mpl/count.hpp"
+#include "boost/mpl/insert.hpp"
+#include <boost/mpl/greater.hpp>
+#include <boost/mpl/for_each.hpp>
+#include <boost/mpl/filter_view.hpp>
+#include "boost/mpl/vector/vector20.hpp"
+#include "boost/assert.hpp"
+#include <boost/type_traits/is_same.hpp>
+
+#include <vector>
+#include <ctime>
+#include <iostream>
+
+#if defined(BOOST_DINKUMWARE_STDLIB) && BOOST_DINKUMWARE_STDLIB < 310
+namespace std { using ::clock_t; }
+#endif
+
+namespace mpl = boost::mpl;
+using namespace mpl::placeholders;
+
+// A metafunction that returns the Event associated with a transition.
+template <class Transition>
+struct transition_event
+{
+ typedef typename Transition::event type;
+};
+
+// A metafunction computing the maximum of a transition's source and
+// end states.
+template <class Transition>
+struct transition_max_state
+{
+ typedef typename mpl::int_<
+ (Transition::current_state > Transition::next_state)
+ ? Transition::current_state
+ : Transition::next_state
+ > type;
+};
+
+template<class Derived>
+class state_machine;
+
+// Generates a singleton runtime lookup table that maps current state
+// to a function that makes the FSM take its transition on the given
+// Event type.
+template <class Fsm, int initial_state, class Stt, class Event>
+struct dispatch_table
+{
+ private:
+ // This is a table of these function pointers.
+ typedef int (*cell)(Fsm&, int, Event const&);
+
+ // Compute the maximum state value in the Fsm so we know how big
+ // to make the table
+ BOOST_STATIC_CONSTANT(
+ int, max_state = (
+ mpl::fold<Stt
+ , mpl::int_<initial_state>
+ , mpl::if_<
+ mpl::greater<transition_max_state<_2>,_1>
+ , transition_max_state<_2>
+ , _1
+ >
+ >::type::value
+ )
+ );
+
+ // A function object for use with mpl::for_each that stuffs
+ // transitions into cells.
+ struct init_cell
+ {
+ init_cell(dispatch_table* self_)
+ : self(self_)
+ {}
+
+ // Cell initializer function object, used with mpl::for_each
+ template <class Transition>
+ void operator()(Transition const&) const
+ {
+ self->entries[Transition::current_state] = &Transition::execute;
+ }
+
+ dispatch_table* self;
+ };
+
+ public:
+ // initialize the dispatch table for a given Event and Fsm
+ dispatch_table()
+ {
+ // Initialize cells for no transition
+ for (int i = 0; i <= max_state; ++i)
+ {
+ // VC7.1 seems to need the two-phase assignment.
+ cell call_no_transition = &state_machine<Fsm>::call_no_transition;
+ entries[i] = call_no_transition;
+ }
+
+ // Go back and fill in cells for matching transitions.
+ mpl::for_each<
+ mpl::filter_view<
+ Stt
+ , boost::is_same<transition_event<_>, Event>
+ >
+ >(init_cell(this));
+ }
+
+ // The singleton instance.
+ static const dispatch_table instance;
+
+ public: // data members
+ cell entries[max_state + 1];
+};
+
+// This declares the statically-initialized dispatch_table instance.
+template <class Fsm, int initial_state, class Stt, class Event>
+const dispatch_table<Fsm, initial_state, Stt, Event>
+dispatch_table<Fsm, initial_state, Stt, Event>::instance;
+
+// CRTP base class for state machines. Pass the actual FSM class as
+// the Derived parameter.
+template<class Derived>
+class state_machine
+{
+ public: // Member functions
+
+ // Main function used by clients of the derived FSM to make
+ // transitions.
+ template<class Event>
+ int process_event(Event const& evt)
+ {
+ typedef typename Derived::transition_table stt;
+ typedef dispatch_table<Derived, Derived::initial_state,stt,Event> table;
+
+ // Call the action
+ return this->m_state
+ = table::instance.entries[this->m_state](
+ *static_cast<Derived*>(this), this->m_state, evt);
+ }
+
+ // Getter that returns the current state of the FSM
+ int current_state() const
+ {
+ return this->m_state;
+ }
+
+ private:
+ template <class Fsm, int initial_state, class Stt, class Event>
+ friend class dispatch_table;
+
+ template <class Event>
+ static int call_no_transition(Derived& fsm, int state, Event const& e)
+ {
+ return fsm.no_transition(state, e);
+ }
+
+ // Default no-transition handler. Can be replaced in the Derived
+ // FSM class.
+ template <class Event>
+ int no_transition(int state, Event const& e)
+ {
+ BOOST_ASSERT(false);
+ return state;
+ }
+
+ protected: // interface for the derived class
+
+ template<class State>
+ state_machine(State state) // Construct with an initial state
+ : m_state(state)
+ {
+ }
+
+ state_machine()
+ : m_state(Derived::initial_state) // Construct with the default initial_state
+ {
+ }
+
+ // Template used to form rows in the transition table
+ template<
+ int CurrentState
+ , class Event
+ , int NextState
+ , void (Derived::*action)(Event const&)
+ >
+ struct row
+ {
+ BOOST_STATIC_CONSTANT(int, current_state = CurrentState);
+ BOOST_STATIC_CONSTANT(int, next_state = NextState);
+ typedef Event event;
+
+ // Take the transition action and return the next state.
+ static int execute(Derived& fsm, int state, Event const& evt)
+ {
+ BOOST_ASSERT(state == current_state);
+ (fsm.*action)(evt);
+ return next_state;
+ }
+ };
+
+ private: // data members
+ int m_state;
+};
+
+namespace // Concrete FSM implementation
+{
+ // events
+ struct play {};
+ struct stop {};
+ struct pause {};
+ struct open_close {};
+
+ // A "complicated" event type that carries some data.
+ struct cd_detected
+ {
+ cd_detected(std::string name, std::vector<std::clock_t> durations)
+ : name(name)
+ , track_durations(durations)
+ {}
+
+ std::string name;
+ std::vector<std::clock_t> track_durations;
+ };
+
+ // Concrete FSM implementation
+ class player : public state_machine<player>
+ {
+ // The list of FSM states
+ enum states {
+ Empty, Open, Stopped, Playing, Paused
+ , initial_state = Empty
+ };
+
+#ifdef __MWERKS__
+ public: // Codewarrior bug workaround. Tested at 0x3202
+#endif
+ // transition actions
+ void start_playback(play const&) { std::cout << "player::start_playback\n"; }
+ void open_drawer(open_close const&) { std::cout << "player::open_drawer\n"; }
+ void close_drawer(open_close const&) { std::cout << "player::close_drawer\n"; }
+ void store_cd_info(cd_detected const&) { std::cout << "player::store_cd_info\n"; }
+ void stop_playback(stop const&) { std::cout << "player::stop_playback\n"; }
+ void pause_playback(pause const&) { std::cout << "player::pause_playback\n"; }
+ void resume_playback(play const&) { std::cout << "player::resume_playback\n"; }
+ void stop_and_open(open_close const&) { std::cout << "player::stop_and_open\n"; }
+#ifdef __MWERKS__
+ private:
+#endif
+ friend class state_machine<player>;
+ typedef player p; // makes transition table cleaner
+
+ // Transition table
+ struct transition_table : mpl::vector11<
+ // Start Event Next Action
+ // +---------+-------------+---------+---------------------+
+ row < Stopped , play , Playing , &p::start_playback >,
+ row < Stopped , open_close , Open , &p::open_drawer >,
+ // +---------+-------------+---------+---------------------+
+ row < Open , open_close , Empty , &p::close_drawer >,
+ // +---------+-------------+---------+---------------------+
+ row < Empty , open_close , Open , &p::open_drawer >,
+ row < Empty , cd_detected , Stopped , &p::store_cd_info >,
+ // +---------+-------------+---------+---------------------+
+ row < Playing , stop , Stopped , &p::stop_playback >,
+ row < Playing , pause , Paused , &p::pause_playback >,
+ row < Playing , open_close , Open , &p::stop_and_open >,
+ // +---------+-------------+---------+---------------------+
+ row < Paused , play , Playing , &p::resume_playback >,
+ row < Paused , stop , Stopped , &p::stop_playback >,
+ row < Paused , open_close , Open , &p::stop_and_open >
+ // +---------+-------------+---------+---------------------+
+ > {};
+
+ // Replaces the default no-transition response.
+ template <class Event>
+ int no_transition(int state, Event const& e)
+ {
+ std::cout << "no transition from state " << state
+ << " on event " << typeid(e).name() << std::endl;
+ return state;
+ }
+ };
+
+ //
+ // Testing utilities.
+ //
+ static char const* const state_names[] = { "Empty", "Open", "Stopped", "Playing", "Paused" };
+
+ void pstate(player const& p)
+ {
+ std::cout << " -> " << state_names[p.current_state()] << std::endl;
+ }
+
+ void test()
+ {
+ player p;
+ p.process_event(open_close()); pstate(p);
+ p.process_event(open_close()); pstate(p);
+ p.process_event(
+ cd_detected(
+ "louie, louie"
+ , std::vector<std::clock_t>( /* track lengths */ )
+ )
+ );
+ pstate(p);
+
+ p.process_event(play()); pstate(p);
+ p.process_event(pause()); pstate(p);
+ p.process_event(play()); pstate(p);
+ p.process_event(stop()); pstate(p);
+ }
+}
+
+int main()
+{
+ test();
+ return 0;
+}