[Home]CPPTM Answers - Exercise 11-4

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

No diff available--this is the first major revision. (no other diffs)
Building upon my version of the solution to 11-0, and using this for my transition table structure:
 
 //      +-----------+--------------------+----------------+------------------------+
 //      | Start     | EntryAction        | ExitAction     |                        |
 //      |           | Event1             | Next1          | Action1                |
 //      |           | Event2             | Next2          | Action2                |
 //      +-----------+--------------------+----------------+------------------------+

Here's the code.  Comments or simplifications appreciated!

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

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

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

template<class StateEntry, class Next>
struct on_exit_dispatcher
{
    typedef typename StateEntry::fsm_t fsm_t;
    
    static void dispatch_on_exit(fsm_t &m, int state)
    {
        if (state == StateEntry::current_state)
        {
            StateEntry::on_exit(m);
        }
        else
        {
            Next::dispatch_on_exit(m, state);
        }
    }
};

template<class StateEntry, class Next>
struct on_entry_dispatcher
{
    typedef typename StateEntry::fsm_t fsm_t;

    static void dispatch_on_entry(fsm_t &m, int state)
    {
        if (state == StateEntry::current_state)
        {
            StateEntry::on_entry(m);
        }
        else
        {
            Next::dispatch_on_entry(m, state);
        }
    }
};

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_execute(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_execute(m, state, e);
        }
    }
};

template <class MyDerivedFSM> class state_machine;

struct default_event_dispatcher
{
    template <class FSM, class Event>
    static int dispatch_execute(state_machine<FSM> &m, int state, Event const &e)
    {
        return m.call_no_transition(state, e);
    }
};
  
struct default_on_entry_dispatcher
{
    template <class FSM>
    static void dispatch_on_entry (state_machine<FSM> &m, int state)
    {}
};


struct default_on_exit_dispatcher
{
    template <class FSM>
    static void dispatch_on_exit (state_machine<FSM> &m, int state)
    {}
};

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_event_dispatcher
:   mpl::fold<
        mpl::filter_view<   
            Table,   
            has_event_entry<_1, Event>
        >,   
        default_event_dispatcher,   
        event_dispatcher<_2, _1, Event> 
    >
{};

template <class Table>
struct generate_on_entry_dispatcher
:   mpl::fold<
        Table,   
        default_on_entry_dispatcher,   
        on_entry_dispatcher<_2, _1> 
    >
{};

template <class Table>
struct generate_on_exit_dispatcher
:   mpl::fold<
        Table,   
        default_on_exit_dispatcher,   
        on_exit_dispatcher<_2, _1> 
    >
{};

template <class MyDerivedFSM>
class state_machine
{
public:
    template <class Event>
    int process_event (Event const &e)
    {
        typedef typename generate_event_dispatcher<
            typename MyDerivedFSM::transition_table, Event>::type event_dispatcher;
        typedef typename generate_on_entry_dispatcher<
            typename MyDerivedFSM::transition_table>::type on_entry_dispatcher;
        typedef typename generate_on_exit_dispatcher<
            typename MyDerivedFSM::transition_table>::type on_exit_dispatcher;

        int newState = event_dispatcher::dispatch_execute(
            *static_cast<MyDerivedFSM*>(this), this->state_, e);
        if (newState != this->state_)
        {
            on_exit_dispatcher::dispatch_on_exit(
                *static_cast<MyDerivedFSM*>(this), this->state_);
            this->state_ = newState;
            on_entry_dispatcher::dispatch_on_entry(
                *static_cast<MyDerivedFSM*>(this), this->state_);
        }
        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, 
                void (MyDerivedFSM::*EntryAction)(),
                void (MyDerivedFSM::*ExitAction)(),
                class VectorOfEvents
             >
    struct in_state
    {
        static int const current_state = CurrentState;
        typedef VectorOfEvents event_vector;
        typedef MyDerivedFSM fsm_t;

        static void on_entry (MyDerivedFSM &fsm)
        {
            if (EntryAction != 0)
            {
                (fsm.*EntryAction)();
            }
        }

        static void on_exit (MyDerivedFSM &fsm)
        {
            if (ExitAction != 0)
            {
                (fsm.*ExitAction)();
            }
        }
    };

    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 &);
    void detect_cd_info();
    void state_entry();
    void state_exit();

    friend class state_machine<Player>;
    typedef Player p;

    struct transition_table : mpl::vector5<
        //      +-----------+--------------------+----------------+------------------------+
        //      | Start     | EntryAction        | ExitAction     |                        |
        //      |           | Event1             | Next1          | Action1                |
        //      |           | Event2             | Next2          | Action2                |
        //      +-----------+--------------------+----------------+------------------------+
        in_state< Stopped   , &p::state_entry    , &p::state_exit , mpl::vector2<
                    on_event< Play               , Playing        , &p::start_playback >,
                    on_event< OpenClose          , Open           , &p::open_drawer    > > >,
        //      +-----------+--------------------+----------------+--------------------+---+
        in_state< Open      , &p::state_entry    , &p::state_exit , mpl::vector1<
                    on_event< OpenClose          , Empty          , &p::close_drawer   > > >,
        //      +-----------+--------------------+----------------+--------------------+---+
        in_state< Empty     , &p::detect_cd_info , &p::state_exit , mpl::vector2<
                    on_event< OpenClose          , Open           , &p::open_drawer    >,
                    on_event< CDDetected         , Stopped        , &p::store_cd_info  > > >,
        //      +-----------+--------------------+----------------+--------------------+---+
        in_state< Playing   , &p::state_entry    , &p::state_exit , 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    , 0                  , 0              , mpl::vector3< 
                    on_event< Play               , Playing        , &p::resume_playback>,
                    on_event< Stop               , Stopped        , &p::stop_playback  >,
                    on_event< OpenClose          , Open           , &p::stop_and_open  > > >
        //      +-----------+--------------------+----------------+--------------------+---+
    > {};
};

inline void 
Player::start_playback (Play const&)
{
    std::cout << "Starting playback" << std::endl;
}

inline void 
Player::open_drawer (OpenClose const&)
{
    std::cout << "Opening drawer" << std::endl;
}

inline void 
Player::close_drawer (OpenClose const&)
{
    std::cout << "Closing drawer" << std::endl;
}

inline void 
Player::store_cd_info (CDDetected const&)
{
    std::cout << "Storing CD Info" << std::endl;
}

inline void 
Player::stop_playback (Stop const&)
{
    std::cout << "Stopping playback" << std::endl;
}

inline void 
Player::pause_playback (Pause const&)
{
    std::cout << "Pausing playback" << std::endl;
}

inline void 
Player::resume_playback (Play const&)
{
    std::cout << "Resuming playback" << std::endl;
}

inline void 
Player::stop_and_open (OpenClose const&)
{
    std::cout << "Stopping playback, opening drawer" << std::endl;
}

inline void 
Player:: state_entry ()
{
    std::cout << "Entering state: " << this->state_ << std::endl;
}

inline void 
Player:: state_exit ()
{
    std::cout << "Leaving state: " << this->state_ << std::endl;
}

inline void 
Player:: detect_cd_info ()
{
    std::cout << "Detecting CD info (in state: " << this->state_ << ")" << std::endl;
    process_event(CDDetected("louie, louie", std::vector<std::clock_t>()));
}


int main(int argc, char* argv[])
{
    Player p;
    p.process_event(Player::OpenClose());
    std::cout << "---------------------" << std::endl;
    p.process_event(Player::OpenClose());
    std::cout << "---------------------" << std::endl;
    p.process_event(Player::OpenClose());
    std::cout << "---------------------" << std::endl;
    p.process_event(Player::OpenClose());
    std::cout << "---------------------" << std::endl;
    p.process_event(Player::Play());
    std::cout << "---------------------" << std::endl;
    p.process_event(Player::Pause());
    std::cout << "---------------------" << std::endl;
    p.process_event(Player::OpenClose());
    std::cout << "---------------------" << std::endl;
    p.process_event(Player::OpenClose());
    std::cout << "---------------------" << std::endl;
    p.process_event(Player::Play());
    std::cout << "---------------------" << std::endl;
    p.process_event(Player::Stop());
    std::cout << "---------------------" << std::endl;
    p.process_event(Player::OpenClose());
    std::cout << "---------------------" << std::endl;
    p.process_event(Player::OpenClose());
    std::cout << "---------------------" << std::endl;
    p.process_event(Player::Play());
    std::cout << "---------------------" << std::endl;
    p.process_event(Player::OpenClose());
    std::cout << "---------------------" << std::endl;
    p.process_event(Player::OpenClose());
    std::cout << "---------------------" << std::endl;
    p.process_event(Player::Play());
    std::cout << "---------------------" << std::endl;
    p.process_event(Player::Stop());
    std::cout << "---------------------" << std::endl;

    int c;
    std::cin >> c;

    return 0;
}



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