diff options
Diffstat (limited to 'src/boost/libs/mpl/example/fsm/player2.cpp')
-rw-r--r-- | src/boost/libs/mpl/example/fsm/player2.cpp | 326 |
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; +} |