From d6d18f7730c131f6a50f3e134c39b860fbf8431d Mon Sep 17 00:00:00 2001 From: Dave Abrahams Date: Mon, 7 Mar 2005 13:37:07 +0000 Subject: [PATCH] Add missing example from book. [SVN r27565] --- example/fsm/player1.cpp | 287 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 287 insertions(+) create mode 100755 example/fsm/player1.cpp diff --git a/example/fsm/player1.cpp b/example/fsm/player1.cpp new file mode 100755 index 0000000..a5a1a2c --- /dev/null +++ b/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 +#include +#include +#include +#include +#include + +#include +#include +#include +namespace mpl = boost::mpl; +using namespace mpl::placeholders; + +#include + +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 state_machine; + +struct default_event_dispatcher +{ + template + static int dispatch( + state_machine& m, int state, Event const& e) + { + return m.call_no_transition(state, e); + } +}; + + + template + struct generate_dispatcher; + +template +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 + int call_no_transition(int state, Event const& e) + { + return static_cast(this) // CRTP downcast + ->no_transition(state, e); + } + // +public: + +template +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(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 + int no_transition(int state, Event const& e) + { + assert(false); + return state; + } +// ... +//// + }; + + +// get the Event associated with a transition. +template +struct transition_event +{ + typedef typename Transition::event type; +}; + +template +struct generate_dispatcher + : mpl::fold< + mpl::filter_view< // select rows triggered by Event + Table + , boost::is_same > + > + , default_event_dispatcher + , event_dispatcher<_2,_1> + > +{}; + + + + struct play {}; + struct open_close {}; + struct cd_detected { + cd_detected(char const*, std::vector 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 +{ + 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; + 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 + , event_dispatcher< + row + , 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( /* track lengths */ ) + ) + ); + p.process_event(play()); // etc. + p.process_event(pause()); + p.process_event(play()); + p.process_event(stop()); + return 0; +}