[Home]CPPTM Answers - Exercise 11-0

BOOST WIKI | RecentChanges | Preferences | Page List | Links List

 
 // (by Ariel Badichi)
 #include <iostream>
 #include <iterator>
 #include <string>
 #include <vector>
 #include <algorithm>
 #include <ctime>
 #include <cassert>
 
 #include <boost/assign.hpp>
 #include <boost/type_traits/is_same.hpp>
 #include <boost/mpl/vector.hpp>
 #include <boost/mpl/fold.hpp>
 #include <boost/mpl/filter_view.hpp>
 #include <boost/mpl/iterator_tags.hpp>
 #include <boost/mpl/iterator_range.hpp>
 #include <boost/mpl/begin.hpp>
 #include <boost/mpl/end.hpp>
 #include <boost/mpl/int.hpp>
 #include <boost/mpl/bool.hpp>
 #include <boost/mpl/deref.hpp>
 #include <boost/mpl/reverse.hpp>
 #include <boost/mpl/placeholders.hpp>
 
 namespace mpl = boost::mpl;
 using namespace mpl::placeholders;
 
 template<typename Derived>
 class state_machine;
 
 namespace details
 {
     template<
         typename Transition,
         typename Next
     >
     struct event_dispatcher
     {
         typedef typename Transition::fsm_t fsm_t;
         typedef typename Transition::event event;
 
         static int dispatch(fsm_t & fsm, int state, const event & e)
         {
             if (state == Transition::current_state) {
                 Transition::execute(fsm, e);
                 return Transition::next_state;
             } else {
                 return Next::dispatch(fsm, state, e);
             }
         }
     };
 
     struct default_event_dispatcher
     {
         template<typename FSM, typename Event>
         static int dispatch(state_machine<FSM> & m, int state, const Event & e)
         {
             return m.call_no_transition(state, e);
         }
     };
 
     template<typename Transition>
     struct transition_event
     {
         typedef typename Transition::event type;
     };
 
     template<typename Row>
     struct get_current_state_
     {
         typedef mpl::int_<Row::current_state> type;
     };
 
     template<typename Iter>
     struct get_current_state
     {
         typedef typename mpl::next<Iter>::type next;
         typedef typename mpl::deref<Iter>::type row;
 
         typedef typename mpl::eval_if<
             typename row::has_current_state,
             get_current_state_<row>,
             get_current_state<next>
         >::type type;
     };
 
     template<typename Iter>
     struct transition_iterator
     {
         typedef mpl::forward_iterator_tag category;
         typedef transition_iterator type;
     };
 
     template<typename Table>
     struct transition_view
         : mpl::iterator_range<
               transition_iterator<typename mpl::begin<Table>::type>,
               transition_iterator<typename mpl::end<Table>::type>
           >
     {};
 
     template<int CurrentState, typename Row>
     struct transition
     {
         const static int current_state = CurrentState;
         const static int next_state = Row::next_state;
         typedef typename Row::event event;
         typedef typename Row::fsm_t fsm_t;
 
         static void execute(fsm_t & fsm, const event & e)
         {
             Row::execute(fsm, e);
         }
     };
 
     template<typename Table, typename Event>
     struct generate_dispatcher
         : mpl::fold<
               mpl::filter_view<
                   // One could use reverse_view from exercise 7-7
                   // to speed things up a little.
                   transition_view<typename mpl::reverse<Table>::type>,
                   boost::is_same<Event, transition_event<_> >
               >,
               default_event_dispatcher,
               event_dispatcher<_2, _1>
           >
     {};
 }
 
 namespace boost
 {
     namespace mpl
     {
         template<typename Iter>
         struct next<details::transition_iterator<Iter> >
             : details::transition_iterator<typename mpl::next<Iter>::type>
         {
         };
 
         template<typename Iter>
         struct deref<details::transition_iterator<Iter> >
         {
             typedef typename deref<Iter>::type row;
             typedef details::transition<
                 details::get_current_state<Iter>::type::value,
                 row
             > type;
         };
     }
 }
 
 template<typename Derived>
 class state_machine
 {
 protected:
     template<
         int CurrentState,
         typename Event,
         int NextState,
         void (Derived::*action)(const Event &)
     >
     struct srow
     {
         typedef mpl::true_ has_current_state;
         static const int current_state = CurrentState;
         static const int next_state = NextState;
         typedef Event event;
         typedef Derived fsm_t;
 
         static void execute(Derived & fsm, const Event & e)
         {
             (fsm.*action)(e);
         }
     };
 
     template<
         typename Event,
         int NextState,
         void (Derived::*action)(const Event &)
     >
     struct row
     {
         typedef mpl::false_ has_current_state;
         static const int next_state = NextState;
         typedef Event event;
         typedef Derived fsm_t;
 
         static void execute(Derived & fsm, const Event & e)
         {
             (fsm.*action)(e);
         }
     };
 
     state_machine()
         : state(Derived::initial_state)
     {
     }
 
     template<typename Event>
     int no_transition(int state, const Event & e)
     {
         assert(0);
         return this->state;
     }
 
 public:
     template<typename Event>
     int process_event(const Event & e)
     {
         typedef typename details::generate_dispatcher<
             Derived::transition_table,
             Event
         >::type dispatcher;
 
         this->state = dispatcher::dispatch(*static_cast<Derived *>(this),
                                            this->state,
                                            e);
 
         return this->state;
     }
 
     template<typename Event>
     int call_no_transition(int state, const Event & e)
     {
         return static_cast<Derived *>(this)->no_transition(state, e);
     }
 
 private:
     int state;
 };
 
 struct play {};
 struct open_close {};
 struct stop {};
 struct pause {};
 class cd_detected
 {
 public:
     typedef std::vector<std::clock_t> lengths_t;
 
 public:
     cd_detected(const std::string & title, const lengths_t & lengths)
     : m_title(&title)
     , m_lengths(&lengths)
     {
     }
 
     const std::string & title() const { return *m_title; }
     const lengths_t & lengths() const { return *m_lengths; }
 
 private:
     const std::string *m_title;
     const lengths_t *m_lengths;
 };
 
 class player : public state_machine<player>
 {
     enum states
     {
         Empty, Open, Stopped, Playing, Paused
         , initial_state = Empty
     };
 
     void start_playback(const play &);
     void open_drawer(const open_close &);
     void close_drawer(const open_close &);
     void store_cd_info(const cd_detected &);
     void stop_playback(const stop &);
     void pause_playback(const pause &);
     void resume_playback(const play &);
     void stop_and_open(const open_close &);
 
     friend class state_machine<player>;
     typedef player p;
 
     struct transition_table : mpl::vector
     //  +-----+---------+-------------+---------+---------------------+
     //  | Row | Start   | Event       | Next    | Action              |
     //  +-----+---------+-------------+---------+---------------------+
         < srow< Stopped , play        , Playing , &p::start_playback  >
         , row <           open_close  , Open    , &p::open_drawer     >
     //  +-----+---------+-------------+---------+---------------------+
         , srow< Open    , open_close  , Empty   , &p::close_drawer    >
     //  +-----+---------+-------------+---------+---------------------+
         , srow< Empty   , open_close  , Open    , &p::open_drawer     >
         , row <           cd_detected , Stopped , &p::store_cd_info   >
     //  +-----+---------+-------------+---------+---------------------+
         , srow< Playing , stop        , Stopped , &p::stop_playback   >
         , row <           pause       , Paused  , &p::pause_playback  >
         , row <           open_close  , Open    , &p::stop_and_open   >
     //  +-----+---------+-------------+---------+---------------------+
         , srow< Paused  , play        , Playing , &p::resume_playback >
         , row <           stop        , Stopped , &p::stop_playback   >
         , row <           open_close  , Open    , &p::stop_and_open   >
     //  +-----+---------+-------------+---------+---------------------+
         > {};
 };
 
 void player::start_playback(const play &)
 {
     std::cout << "Starting playback...\n";
 }
 
 void player::open_drawer(const open_close &)
 {
     std::cout << "Opening drawer...\n";
 }
 
 void player::close_drawer(const open_close &)
 {
     std::cout << "Closing drawer...\n";
 }
 
 void player::store_cd_info(const cd_detected & cd)
 {
     std::cout << "CD info - title: \"" << cd.title() << "\" track lengths: ";
     std::ostream_iterator<std::clock_t> out(std::cout, " ");
     const cd_detected::lengths_t & lengths = cd.lengths();
     std::copy(lengths.begin(), lengths.end(), out);
     std::cout << "\n";
 }
 
 void player::stop_playback(const stop &)
 {
     std::cout << "Stopping playback...\n";
 }
 
 void player::pause_playback(const pause &)
 {
     std::cout << "Pausing playback...\n";
 }
 
 void player::resume_playback(const play &)
 {
     std::cout << "Resuming playback...\n";
 }
 
 void player::stop_and_open(const open_close &)
 {
     std::cout << "Stopping playback and opening drawer...\n";
 }
 
 int main()
 {
     using namespace boost::assign;
 
     player p;
 
     p.process_event(open_close());
     p.process_event(open_close());
     cd_detected::lengths_t track_lengths;
     track_lengths += 10, 20, 30, 60;
     p.process_event(cd_detected("louie, louie", track_lengths));
     p.process_event(play());
     p.process_event(pause());
     p.process_event(play());
     p.process_event(stop());
 
     return 0;
 }

------------------------------------------------
Addendum by John Wismar

I approached this problem differently, because I wanted a different transition 
table format, that looked like this:
 //      +-----------+----------------------------------------------+
 //      | Start     |                                              | 
 //      |           +-----------+---------+--------------------+   | 
 //      |           |Event1     | Next1   | Action1            |   |
 //      |           |Event2     | Next2   | Action2            |   |
 //      +-----------+-----------+---------+--------------------+---+

Here's the source for my implementation.  Comments welcome!!

#include <boost/mpl/vector.hpp>
#include <boost/mpl/fold.hpp>
#include <boost/mpl/filter_view.hpp>
#include <boost/mpl/placeholders.hpp>

#include <ctime>
#include <vector>
#include <cassert>

namespace mpl = boost::mpl;
using namespace mpl::placeholders;

template <class OnEventEntry>
struct on_event_entry_event
{
    typedef typename OnEventEntry::event type;
};

template<class StateEntry, class Next, class Event>
struct event_dispatcher
{
    typedef typename StateEntry::event_vector event_vector;
    typedef typename mpl::find_if<event_vector, 
        boost::is_same<Event, on_event_entry_event<_1> > >::type event_entry_iter;
    typedef typename mpl::deref<event_entry_iter>::type event_entry;
    typedef typename event_entry::fsm_t fsm_t;

    static int dispatch(fsm_t &m, int state, Event const &e)
    {
        if (state == StateEntry::current_state)
        {
            event_entry::execute(m, e);
            return event_entry::next_state;
        }
        else
        {
            return Next::dispatch(m, state, e);
        }
    }
};

template <class MyDerivedFSM> 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 StateEntry, class Event>
struct has_event_entry
{
    typedef typename StateEntry::event_vector event_vector;
    typedef typename mpl::find_if<event_vector, 
        boost::is_same<Event, on_event_entry_event<_1> > >::type event_entry_iter;
    typedef typename boost::is_same<event_entry_iter, 
        typename mpl::end<event_vector>::type>::type iter_is_end;
    typedef typename boost::is_same<boost::false_type, iter_is_end>::type type;
};

template <class Table, class Event>
struct generate_dispatcher
:   mpl::fold<  mpl::filter_view<   Table,
                                    has_event_entry<_1, Event> >,   
                default_event_dispatcher,   
                event_dispatcher<_2, _1, Event> >
{};

template <class MyDerivedFSM>
class state_machine
{
public:
    template <class Event>
    int process_event (Event const &e)
    {
        typedef typename generate_dispatcher<
            typename MyDerivedFSM::transition_table, Event>::type dispatcher;

        this->state_ = dispatcher::dispatch(*static_cast<MyDerivedFSM*>(this), this->state_, e);
        return this->state_;
    }

    template <class Event>
    int no_transition (int state, Event const &e)
    {
        assert(false);
        return state;
    }

    template <class Event>
    int call_no_transition (int state, Event const &e)
    {
        return static_cast<MyDerivedFSM*>(this)->no_transition(state, e);
    }

protected:
    state_machine () : state_(MyDerivedFSM::initial_state){}
    ~state_machine () {}

    template <class Event, int NextState, void (MyDerivedFSM::*action)(Event const&)>
    struct on_event
    {
        static int const next_state = NextState;
        typedef Event event;
        typedef MyDerivedFSM fsm_t;

        static void execute (MyDerivedFSM &fsm, Event const &e)
        {
            (fsm.*action)(e);
        }
    };

    template <int CurrentState, class VectorOfEvents>
    struct in_state
    {
        static int const current_state = CurrentState;
        typedef VectorOfEvents event_vector;
    };

private:
    int state_;
};


class Player : public state_machine<Player>
{
public:
    Player(){}
    ~Player(){}

    //Events
    struct Play {};
    struct OpenClose {};
    struct CDDetected 
    {
        CDDetected(char const*, std::vector<std::clock_t> const&) {}
    };
    struct Stop {};
    struct Pause {};

private:

    //FSM states
    enum states
    {
        Empty,
        Open,
        Stopped,
        Playing,
        Paused,
        initial_state=Empty
    };

    //FSM Transition actions
    void start_playback(Play const&){}
    void open_drawer(OpenClose const&){}
    void close_drawer(OpenClose const&){}
    void store_cd_info(CDDetected const &){}
    void stop_playback(Stop const&){}
    void pause_playback(Pause const &){}
    void resume_playback(Play const&){}
    void stop_and_open(OpenClose const &){}

    friend class state_machine<Player>;
    typedef Player p;

    struct transition_table : mpl::vector5<
        //      +-----------+----------------------------------------------+
        //      | Start     |                                              | 
        //      |           +-----------+---------+--------------------+   | 
        //      |           | Event1    | Next1   | Action1            |   |
        //      |           | Event2    | Next2   | Action2            |   |
        //      +-----------+-----------+---------+--------------------+---+
        in_state< Stopped   , mpl::vector2<
                    on_event< Play      , Playing , &p::start_playback >,
                    on_event< OpenClose , Open    , &p::open_drawer    > > >,
        //      +-----------+-----------+---------+--------------------+---+
        in_state< Open      , mpl::vector1<
                    on_event< OpenClose , Empty   , &p::close_drawer   > > >,
        //      +-----------+-----------+---------+--------------------+---+
        in_state< Empty     , mpl::vector2<
                    on_event< OpenClose , Open    , &p::open_drawer    >,
                    on_event< CDDetected, Stopped , &p::store_cd_info  > > >,
        //      +-----------+-----------+---------+--------------------+---+
        in_state< Playing   , mpl::vector3< 
                    on_event< Pause     , Paused  , &p::pause_playback >,
                    on_event< Stop      , Stopped , &p::stop_playback  >,
                    on_event< OpenClose , Open    , &p::stop_and_open  > > >,
        //      +-----------+-----------+---------+--------------------+---+
        in_state< Paused    , mpl::vector3< 
                    on_event< Play      , Playing , &p::resume_playback>,
                    on_event< Stop      , Stopped , &p::stop_playback  >,
                    on_event< OpenClose , Open    , &p::stop_and_open  > > >
        //      +-----------+-----------+---------+--------------------+---+
    > {};
};


int main(int argc, char* argv[])
{
    Player p;
    p.process_event(Player::OpenClose());
    p.process_event(Player::OpenClose());
    p.process_event(Player::CDDetected("louie, louie", std::vector<std::clock_t>()));
    p.process_event(Player::Play());
    p.process_event(Player::Pause());
    p.process_event(Player::Play());
    p.process_event(Player::Stop());

    return 0;
}



BOOST WIKI | RecentChanges | Preferences | Page List | Links List
Edit text of this page | View other revisions
Last edited August 31, 2005 9:03 am (diff)
Search:
Disclaimer: This site not officially maintained by Boost Developers