diff options
Diffstat (limited to 'src/boost/libs/mpl/example/fsm')
-rw-r--r-- | src/boost/libs/mpl/example/fsm/README.txt | 28 | ||||
-rw-r--r-- | src/boost/libs/mpl/example/fsm/aux_/STT_impl_gen.hpp | 145 | ||||
-rw-r--r-- | src/boost/libs/mpl/example/fsm/aux_/base_event.hpp | 59 | ||||
-rw-r--r-- | src/boost/libs/mpl/example/fsm/aux_/event.hpp | 54 | ||||
-rw-r--r-- | src/boost/libs/mpl/example/fsm/aux_/state.hpp | 42 | ||||
-rw-r--r-- | src/boost/libs/mpl/example/fsm/aux_/transition.hpp | 47 | ||||
-rw-r--r-- | src/boost/libs/mpl/example/fsm/player.cpp | 76 | ||||
-rw-r--r-- | src/boost/libs/mpl/example/fsm/player1.cpp | 287 | ||||
-rw-r--r-- | src/boost/libs/mpl/example/fsm/player2.cpp | 326 | ||||
-rw-r--r-- | src/boost/libs/mpl/example/fsm/state_machine.hpp | 145 |
10 files changed, 1209 insertions, 0 deletions
diff --git a/src/boost/libs/mpl/example/fsm/README.txt b/src/boost/libs/mpl/example/fsm/README.txt new file mode 100644 index 00000000..0f4863fe --- /dev/null +++ b/src/boost/libs/mpl/example/fsm/README.txt @@ -0,0 +1,28 @@ +What's In This Directory +======================== + +* player1.cpp - this is exactly what's covered in C++ Template + Metaprogramming (http://www.boost-consulting.com/mplbook); in fact, + it was auto-extracted from the examples as shown in the book. The + state machine framework and its use are together in one .cpp file; + normally they would be separated. You can think of the framework as + ending with the definition of the generate_dispatcher class + template. + + You can ignore the typedef called "dummy;" that was included in order to + test an intermediate example that appears in the book. + +* player2.cpp - this example demonstrates that the abstraction of the + framework is complete by replacing its implementation with one that + dispatches using O(1) table lookups, while still using the same code + to describe the particular FSM. Look at this one if you want to see + how to generate a static lookup table that's initialized at dynamic + initialization time. + +* player.cpp, state_machine.hpp - This example predates the book, and + is more sophisticated in some ways than what we cover in the other + examples. In particular, it supports state invariants, and it + maintains an internal event queue, which requires an additional + layer of runtime dispatching to sort out the next event to be + processed. + diff --git a/src/boost/libs/mpl/example/fsm/aux_/STT_impl_gen.hpp b/src/boost/libs/mpl/example/fsm/aux_/STT_impl_gen.hpp new file mode 100644 index 00000000..1fd0a5ff --- /dev/null +++ b/src/boost/libs/mpl/example/fsm/aux_/STT_impl_gen.hpp @@ -0,0 +1,145 @@ + +#ifndef BOOST_FSM_HANDLER_INCLUDED +#define BOOST_FSM_HANDLER_INCLUDED + +// Copyright Aleksey Gurtovoy 2002-2004 +// +// 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) +// +// See http://www.boost.org/libs/mpl for documentation. + +// $Id$ +// $Date$ +// $Revision$ + +#include <boost/mpl/if.hpp> +#include <boost/mpl/fold.hpp> +#include <boost/mpl/front.hpp> +#include <boost/type_traits/is_same.hpp> + +#include <typeinfo> +#include <cassert> + +namespace fsm { namespace aux { + +namespace mpl = boost::mpl; +using namespace mpl::placeholders; + +template< typename Transition > +struct STT_void_row_impl +{ + typedef typename Transition::from_state_t state_t; + typedef typename Transition::fsm_t fsm_t; + typedef typename Transition::base_event_t base_event_t; + + static long do_process_event(fsm_t&, long state, base_event_t const&) + { + assert(false); + return state; + } + + static long do_transition(fsm_t&, long state, base_event_t const&) + { + assert(false); + return state; + } +}; + + +template< + typename PrevRowImpl + , typename Transition + > +struct STT_event_row_impl + : PrevRowImpl +{ + typedef typename Transition::from_state_t state_t; + typedef typename Transition::fsm_t fsm_t; + typedef typename Transition::base_event_t base_event_t; + + static long do_process_event(fsm_t& fsm, long state, base_event_t const& evt) + { + if (typeid(typename Transition::event_t) == typeid(evt)) + { + // typedefs are here to make GCC happy + typedef typename Transition::to_state_t to_state_; + typedef typename Transition::from_state_t from_state_; + + return Transition::do_transition(fsm, evt) + ? to_state_::do_check_invariant(fsm) + : from_state_::do_check_invariant(fsm) + ; + } + + return PrevRowImpl::do_process_event(fsm, state, evt); + } +}; + +template< + typename PrevRowImpl + , typename StateType + > +struct STT_state_row_impl + : PrevRowImpl +{ + typedef typename PrevRowImpl::fsm_t fsm_t; + typedef typename PrevRowImpl::base_event_t base_event_t; + + static long do_transition(fsm_t& fsm, long state, base_event_t const& evt) + { + return state == StateType::value + ? PrevRowImpl::do_process_event(fsm, state, evt) + : PrevRowImpl::do_transition(fsm, state, evt) + ; + } + + static long do_process_event(fsm_t&, long state, base_event_t const&) + { + assert(false); + return state; + } +}; + +template< + typename PrevRowImpl + , typename Transition + > +struct STT_row_impl +{ + typedef typename mpl::if_< + boost::is_same< + typename PrevRowImpl::state_t + , typename Transition::from_state_t + > + , STT_event_row_impl< PrevRowImpl,Transition > + , STT_event_row_impl< + STT_state_row_impl< PrevRowImpl,typename PrevRowImpl::state_t > + , Transition + > + >::type type; +}; + + +template< typename Transitions > +struct STT_impl_gen +{ + private: + typedef typename mpl::front<Transitions>::type first_; + typedef typename mpl::fold< + Transitions + , STT_void_row_impl<first_> + , STT_row_impl<_,_> + >::type STT_impl_; + + public: + typedef STT_state_row_impl< + STT_impl_ + , typename STT_impl_::state_t + > type; +}; + +}} + +#endif // BOOST_FSM_HANDLER_INCLUDED diff --git a/src/boost/libs/mpl/example/fsm/aux_/base_event.hpp b/src/boost/libs/mpl/example/fsm/aux_/base_event.hpp new file mode 100644 index 00000000..99df7d9d --- /dev/null +++ b/src/boost/libs/mpl/example/fsm/aux_/base_event.hpp @@ -0,0 +1,59 @@ + +#ifndef BOOST_FSM_BASE_EVENT_INCLUDED +#define BOOST_FSM_BASE_EVENT_INCLUDED + +// Copyright Aleksey Gurtovoy 2002-2004 +// +// 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) +// +// See http://www.boost.org/libs/mpl for documentation. + +// $Id$ +// $Date$ +// $Revision$ + +#include <memory> +#include <boost/config.hpp> + +namespace fsm { namespace aux { + +// represent an abstract base for FSM events + +struct base_event +{ + public: + virtual ~base_event() {}; + +#if defined(BOOST_NO_CXX11_SMART_PTR) + + std::auto_ptr<base_event> clone() const + +#else + + std::unique_ptr<base_event> clone() const + +#endif + + { + return do_clone(); + } + + private: + +#if defined(BOOST_NO_CXX11_SMART_PTR) + + virtual std::auto_ptr<base_event> do_clone() const = 0; + +#else + + virtual std::unique_ptr<base_event> do_clone() const = 0; + +#endif + +}; + +}} + +#endif // BOOST_FSM_BASE_EVENT_INCLUDED diff --git a/src/boost/libs/mpl/example/fsm/aux_/event.hpp b/src/boost/libs/mpl/example/fsm/aux_/event.hpp new file mode 100644 index 00000000..895bd7a6 --- /dev/null +++ b/src/boost/libs/mpl/example/fsm/aux_/event.hpp @@ -0,0 +1,54 @@ + +#ifndef BOOST_FSM_EVENT_INCLUDED +#define BOOST_FSM_EVENT_INCLUDED + +// Copyright Aleksey Gurtovoy 2002-2004 +// +// 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) +// +// See http://www.boost.org/libs/mpl for documentation. + +// $Id$ +// $Date$ +// $Revision$ + +#include "base_event.hpp" + +namespace fsm { namespace aux { + +template< typename Derived > +struct event + : base_event +{ + public: + typedef base_event base_t; + + private: + +#if defined(BOOST_NO_CXX11_SMART_PTR) + + virtual std::auto_ptr<base_event> do_clone() const + { + return std::auto_ptr<base_event>( + new Derived(static_cast<Derived const&>(*this)) + ); + } + +#else + + virtual std::unique_ptr<base_event> do_clone() const + { + return std::unique_ptr<base_event>( + new Derived(static_cast<Derived const&>(*this)) + ); + } + +#endif + +}; + +}} + +#endif // BOOST_FSM_EVENT_INCLUDED diff --git a/src/boost/libs/mpl/example/fsm/aux_/state.hpp b/src/boost/libs/mpl/example/fsm/aux_/state.hpp new file mode 100644 index 00000000..bbe2bbf3 --- /dev/null +++ b/src/boost/libs/mpl/example/fsm/aux_/state.hpp @@ -0,0 +1,42 @@ + +#ifndef BOOST_FSM_STATE_INCLUDED +#define BOOST_FSM_STATE_INCLUDED + +// Copyright Aleksey Gurtovoy 2002-2004 +// +// 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) +// +// See http://www.boost.org/libs/mpl for documentation. + +// $Id$ +// $Date$ +// $Revision$ + +#include <boost/mpl/integral_c.hpp> + +namespace fsm { namespace aux { + +namespace mpl = boost::mpl; + +// represent a FSM state + +template< + typename T + , long State + , void (T::* invariant_func)() const + > +struct state + : mpl::integral_c<long,State> +{ + static long do_check_invariant(T const& x) + { + if (invariant_func) (x.*invariant_func)(); + return State; + } +}; + +}} + +#endif // BOOST_FSM_STATE_INCLUDED diff --git a/src/boost/libs/mpl/example/fsm/aux_/transition.hpp b/src/boost/libs/mpl/example/fsm/aux_/transition.hpp new file mode 100644 index 00000000..842e5e08 --- /dev/null +++ b/src/boost/libs/mpl/example/fsm/aux_/transition.hpp @@ -0,0 +1,47 @@ + +#ifndef BOOST_FSM_TRANSITION_INCLUDED +#define BOOST_FSM_TRANSITION_INCLUDED + +// Copyright Aleksey Gurtovoy 2002-2004 +// +// 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) +// +// See http://www.boost.org/libs/mpl for documentation. + +// $Id$ +// $Date$ +// $Revision$ + +#include <cassert> + +namespace fsm { namespace aux { + +// represent a signle transition between states |From| and |To| + +template< + typename T + , typename From + , typename Event + , typename To + , bool (T::* transition_func)(Event const&) + > +struct transition +{ + typedef T fsm_t; + typedef From from_state_t; + typedef Event event_t; + typedef To to_state_t; + + typedef typename Event::base_t base_event_t; + static bool do_transition(T& x, base_event_t const& e) + { + assert(dynamic_cast<event_t const*>(&e) == &e); + return (x.*transition_func)(static_cast<event_t const &>(e)); + } +}; + +}} + +#endif // BOOST_FSM_TRANSITION_INCLUDED diff --git a/src/boost/libs/mpl/example/fsm/player.cpp b/src/boost/libs/mpl/example/fsm/player.cpp new file mode 100644 index 00000000..c3a5a124 --- /dev/null +++ b/src/boost/libs/mpl/example/fsm/player.cpp @@ -0,0 +1,76 @@ + +// Copyright Aleksey Gurtovoy 2002-2004 +// +// 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) +// +// See http://www.boost.org/libs/mpl for documentation. + +// $Id$ +// $Date$ +// $Revision$ + +#include "state_machine.hpp" +#include <boost/mpl/list.hpp> + +#include <iostream> + +namespace mpl = boost::mpl; + +class player + : public fsm::state_machine<player> +{ + public: + player() {} + + // events + struct play_event : event<play_event> {}; + struct stop_event : event<stop_event> {}; + struct pause_event : event<pause_event> {}; + +// MWCW 8.1 is too eager in inforcing access for non-type template parameters +// private: + typedef player self_t; + + // state invariants + void stopped_state_invariant() const {} + void playing_state_invariant() const {} + void paused_state_invariant() const {} + + // states (invariants are passed as non-type template arguments) + typedef state<0, &self_t::stopped_state_invariant> stopped; + typedef state<1, &self_t::playing_state_invariant> playing; + typedef state<2, &self_t::paused_state_invariant> paused; + +// private: + + // transition functions + bool do_play(play_event const&) { std::cout << "player::do_play\n"; return true; } + bool do_stop(stop_event const&) { std::cout << "player::do_stop\n"; return true; } + bool do_pause(pause_event const&) { std::cout << "player::do_pause\n"; return true; } + bool do_resume(play_event const&) { std::cout << "player::do_resume\n"; return true; } + + // transitions, in the following format: + // | current state | event | next state | transition function | + friend class fsm::state_machine<player>; + typedef mpl::list< + transition<stopped, play_event, playing, &player::do_play> + , transition<playing, stop_event, stopped, &player::do_stop> + , transition<playing, pause_event, paused, &player::do_pause> + , transition<paused, play_event, playing, &player::do_resume> + , transition<paused, stop_event, stopped, &player::do_stop> + >::type transition_table; + + typedef stopped initial_state; +}; + +int main() +{ + player p; + p.process_event(player::play_event()); + p.process_event(player::pause_event()); + p.process_event(player::play_event()); + p.process_event(player::stop_event()); + return 0; +} diff --git a/src/boost/libs/mpl/example/fsm/player1.cpp b/src/boost/libs/mpl/example/fsm/player1.cpp new file mode 100644 index 00000000..a5a1a2cb --- /dev/null +++ b/src/boost/libs/mpl/example/fsm/player1.cpp @@ -0,0 +1,287 @@ +/* + + Copyright David Abrahams 2003-2004 + Copyright Aleksey Gurtovoy 2003-2004 + + 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) + + This file was automatically extracted from the source of + "C++ Template Metaprogramming", by David Abrahams and + Aleksey Gurtovoy. + + It was built successfully with GCC 3.4.2 on Windows using + the following command: + + g++ -I..\..\boost_1_32_0 -o%TEMP%\metaprogram-chapter11-example16.exe example16.cpp + + +*/ +#include <boost/mpl/fold.hpp> +#include <boost/mpl/filter_view.hpp> +#include <boost/type_traits/is_same.hpp> +#include <vector> +#include <ctime> +#include <boost/mpl/vector.hpp> + +#include <boost/mpl/placeholders.hpp> +#include <boost/mpl/assert.hpp> +#include <boost/static_assert.hpp> +namespace mpl = boost::mpl; +using namespace mpl::placeholders; + +#include <cassert> + +template< + class Transition + , class Next +> +struct event_dispatcher +{ + typedef typename Transition::fsm_t fsm_t; + typedef typename Transition::event event; + + static int dispatch( + fsm_t& fsm, int state, event const& e) + { + if (state == Transition::current_state) + { + Transition::execute(fsm, e); + return Transition::next_state; + } + else // move on to the next node in the chain. + { + return Next::dispatch(fsm, state, e); + } + } +}; + + + +template <class Derived> class state_machine; + +struct default_event_dispatcher +{ + template<class FSM, class Event> + static int dispatch( + state_machine<FSM>& m, int state, Event const& e) + { + return m.call_no_transition(state, e); + } +}; + + + template<class Table, class Event> + struct generate_dispatcher; + +template<class Derived> +class state_machine +{ + // ... + protected: + template< + int CurrentState + , class Event + , int NextState + , void (Derived::*action)(Event const&) + > + struct row + { + // for later use by our metaprogram + static int const current_state = CurrentState; + static int const next_state = NextState; + typedef Event event; + typedef Derived fsm_t; + + // do the transition action. + static void execute(Derived& fsm, Event const& e) + { + (fsm.*action)(e); + } + }; + + + friend class default_event_dispatcher; + + template <class Event> + int call_no_transition(int state, Event const& e) + { + return static_cast<Derived*>(this) // CRTP downcast + ->no_transition(state, e); + } + // +public: + +template<class Event> +int process_event(Event const& evt) +{ + // generate the dispatcher type. + typedef typename generate_dispatcher< + typename Derived::transition_table, Event + >::type dispatcher; + + // dispatch the event. + this->state = dispatcher::dispatch( + *static_cast<Derived*>(this) // CRTP downcast + , this->state + , evt + ); + + // return the new state + return this->state; +} + +// ... + protected: + state_machine() + : state(Derived::initial_state) + { + } + + private: + int state; +// ... + +// ... + public: + template <class Event> + int no_transition(int state, Event const& e) + { + assert(false); + return state; + } +// ... +//// + }; + + +// get the Event associated with a transition. +template <class Transition> +struct transition_event +{ + typedef typename Transition::event type; +}; + +template<class Table, class Event> +struct generate_dispatcher + : mpl::fold< + mpl::filter_view< // select rows triggered by Event + Table + , boost::is_same<Event, transition_event<_1> > + > + , default_event_dispatcher + , event_dispatcher<_2,_1> + > +{}; + + + + struct play {}; + struct open_close {}; + struct cd_detected { + cd_detected(char const*, std::vector<std::clock_t> const&) {} + }; + #ifdef __GNUC__ // in which pause seems to have a predefined meaning + # define pause pause_ + #endif + struct pause {}; + struct stop {}; + + +// concrete FSM implementation +class player : public state_machine<player> +{ + private: + // 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 + + void start_playback(play const&); + void open_drawer(open_close const&); + void close_drawer(open_close const&); + void store_cd_info(cd_detected const&); + void stop_playback(stop const&); + void pause_playback(pause const&); + void resume_playback(play const&); + void stop_and_open(open_close const&); + + + #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 > + // +---------+-------------+---------+---------------------+ + + > {}; +typedef + +event_dispatcher< + row<Stopped, play, Playing, &player::start_playback> + , event_dispatcher< + row<Paused, play, Playing, &player::resume_playback> + , default_event_dispatcher + > +> + dummy; +}; + + void player::start_playback(play const&){} + void player::open_drawer(open_close const&){} + void player::close_drawer(open_close const&){} + void player::store_cd_info(cd_detected const&){} + void player::stop_playback(stop const&){} + void player::pause_playback(pause const&){} + void player::resume_playback(play const&){} + void player::stop_and_open(open_close const&){} + + + + +int main() +{ + player p; // An instance of the FSM + + p.process_event(open_close()); // user opens CD player + p.process_event(open_close()); // inserts CD and closes + p.process_event( // CD is detected + cd_detected( + "louie, louie" + , std::vector<std::clock_t>( /* track lengths */ ) + ) + ); + p.process_event(play()); // etc. + p.process_event(pause()); + p.process_event(play()); + p.process_event(stop()); + return 0; +} 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 00000000..8704ef5d --- /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; +} diff --git a/src/boost/libs/mpl/example/fsm/state_machine.hpp b/src/boost/libs/mpl/example/fsm/state_machine.hpp new file mode 100644 index 00000000..4bfd0169 --- /dev/null +++ b/src/boost/libs/mpl/example/fsm/state_machine.hpp @@ -0,0 +1,145 @@ + +#ifndef BOOST_FSM_STATE_MACHINE_INCLUDED +#define BOOST_FSM_STATE_MACHINE_INCLUDED + +// Copyright Aleksey Gurtovoy 2002-2004 +// +// 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) +// +// See http://www.boost.org/libs/mpl for documentation. + +// $Id$ +// $Date$ +// $Revision$ + +#include "aux_/event.hpp" +#include "aux_/state.hpp" +#include "aux_/transition.hpp" +#include "aux_/STT_impl_gen.hpp" +#include <boost/shared_ptr.hpp> + +#include <queue> +#include <memory> +#include <cassert> + +namespace fsm { + +template< typename Derived > +class state_machine +{ + private: + typedef state_machine self_t; + typedef aux::base_event base_event_t; + typedef boost::shared_ptr<base_event_t const> base_event_ptr_t; + + public: + typedef long state_t; + typedef void (Derived::* invariant_func_t)() const; + + template< typename DerivedEvent > + struct event + : aux::event<DerivedEvent> + { + }; + + void process_event(base_event_t const& evt) + { + // all internal events should be handled at this point + assert(!m_events_queue.size()); + + // process the external event passed + do_transition(evt); + + // if the previous transition generated any internal events, + // process those + while (m_events_queue.size()) + { + do_transition(*m_events_queue.front()); + m_events_queue.pop(); + } + } + + state_t current_state() const + { + return m_state; + } + + protected: + // interface for the derived class + + state_machine(state_t const& initial_state) + : m_state(initial_state) + { + } + + state_machine() + : m_state(typename Derived::initial_state()) + { + } + + virtual ~state_machine() + { + } + + +#if defined(BOOST_NO_CXX11_SMART_PTR) + + void post_event(std::auto_ptr<base_event_t const> evt) + +#else + + void post_event(std::unique_ptr<base_event_t const> evt) + +#endif + + { + m_events_queue.push(base_event_ptr_t(evt.release())); + } + + template< + long State +#if !defined(BOOST_INTEL_CXX_VERSION) && (!defined(__GNUC__) || __GNUC__ >= 3) + , invariant_func_t f = static_cast<invariant_func_t>(0) +#else + , invariant_func_t f = 0 +#endif + > + struct state + : fsm::aux::state<Derived,State,f> + { + }; + + template< + typename From + , typename Event + , typename To + , bool (Derived::* transition_func)(Event const&) + > + struct transition + : aux::transition< Derived,From,Event,To,transition_func > + { + }; + + private: + + void do_transition(base_event_t const& evt) + { + typedef typename Derived::transition_table STT_; + typedef typename aux::STT_impl_gen< STT_ >::type STT_impl_; + + m_state = STT_impl_::do_transition( + static_cast<Derived&>(*this) + , m_state + , evt + ); + } + + state_t m_state; + std::queue< base_event_ptr_t > m_events_queue; +}; + +} // namespace fsm + +#endif // BOOST_FSM_STATE_MACHINE_INCLUDED |