#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 {
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
{
static int const current_state = CurrentState;
static int const next_state = NextState;
typedef Event event;
typedef Derived fsm_t;
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) ->no_transition(state, e);
}
public:
template<class Event>
int process_event(Event const& evt)
{
typedef typename generate_dispatcher<
typename Derived::transition_table, Event
>::type dispatcher;
this->state = dispatcher::dispatch(
*static_cast<Derived*>(this) , this->state
, evt
);
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;
}
};
template <class Transition>
struct transition_event
{
typedef typename Transition::event type;
};
template<class Table, class Event>
struct generate_dispatcher
: mpl::fold<
mpl::filter_view< 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__
# define pause pause_
#endif
struct pause {};
struct stop {};
class player : public state_machine<player>
{
private:
enum states {
Empty, Open, Stopped, Playing, Paused
, initial_state = Empty
};
#ifdef __MWERKS__
public: #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;
struct transition_table : mpl::vector11<
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;
p.process_event(open_close()); p.process_event(open_close()); p.process_event( cd_detected(
"louie, louie"
, std::vector<std::clock_t>( )
)
);
p.process_event(play()); p.process_event(pause());
p.process_event(play());
p.process_event(stop());
return 0;
}