Chris@16: // Copyright 2008 Christophe Henry Chris@16: // henry UNDERSCORE christophe AT hotmail DOT com Chris@16: // This is an extended version of the state machine available in the boost::mpl library Chris@16: // Distributed under the same license as the original. Chris@16: // Copyright for the original version: Chris@16: // Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed Chris@16: // under the Boost Software License, Version 1.0. (See accompanying Chris@16: // file LICENSE_1_0.txt or copy at Chris@16: // http://www.boost.org/LICENSE_1_0.txt) Chris@16: Chris@16: #ifndef BOOST_MSM_BACK_DISPATCH_TABLE_H Chris@16: #define BOOST_MSM_BACK_DISPATCH_TABLE_H Chris@16: Chris@16: #include Chris@16: Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: Chris@16: #include Chris@16: #include Chris@16: Chris@16: #include Chris@16: #include Chris@16: #include Chris@16: Chris@16: BOOST_MPL_HAS_XXX_TRAIT_DEF(is_frow) Chris@16: Chris@16: namespace boost { namespace msm { namespace back Chris@16: { Chris@16: Chris@16: // Generates a singleton runtime lookup table that maps current state Chris@16: // to a function that makes the SM take its transition on the given Chris@16: // Event type. Chris@16: template Chris@16: struct dispatch_table Chris@16: { Chris@16: private: Chris@16: // This is a table of these function pointers. Chris@16: typedef HandledEnum (*cell)(Fsm&, int,int,Event const&); Chris@16: typedef bool (*guard)(Fsm&, Event const&); Chris@16: Chris@16: // class used to build a chain (or sequence) of transitions for a given event and start state Chris@16: // (like an UML diamond). Allows transition conflicts. Chris@16: template< typename Seq,typename AnEvent,typename State > Chris@16: struct chain_row Chris@16: { Chris@16: typedef State current_state_type; Chris@16: typedef AnEvent transition_event; Chris@16: Chris@16: // helper for building a disable/enable_if-controlled execute function Chris@16: struct execute_helper Chris@16: { Chris@16: template Chris@16: static Chris@16: HandledEnum Chris@16: execute(Fsm& , int, int, Event const& , ::boost::mpl::true_ const & ) Chris@16: { Chris@16: // if at least one guard rejected, this will be ignored, otherwise will generate an error Chris@16: return HANDLED_FALSE; Chris@16: } Chris@16: Chris@16: template Chris@16: static Chris@16: HandledEnum Chris@16: execute(Fsm& fsm, int region_index , int state, Event const& evt, Chris@16: ::boost::mpl::false_ const & ) Chris@16: { Chris@16: // try the first guard Chris@16: typedef typename ::boost::mpl::front::type first_row; Chris@16: HandledEnum res = first_row::execute(fsm,region_index,state,evt); Chris@16: if (HANDLED_TRUE!=res && HANDLED_DEFERRED!=res) Chris@16: { Chris@16: // if the first rejected, move on to the next one Chris@16: HandledEnum sub_res = Chris@16: execute::type>(fsm,region_index,state,evt, Chris@16: ::boost::mpl::bool_< Chris@16: ::boost::mpl::empty::type>::type::value>()); Chris@16: // if at least one guards rejects, the event will not generate a call to no_transition Chris@16: if ((HANDLED_FALSE==sub_res) && (HANDLED_GUARD_REJECT==res) ) Chris@16: return HANDLED_GUARD_REJECT; Chris@16: else Chris@16: return sub_res; Chris@16: } Chris@16: return res; Chris@16: } Chris@16: }; Chris@16: // Take the transition action and return the next state. Chris@16: static HandledEnum execute(Fsm& fsm, int region_index, int state, Event const& evt) Chris@16: { Chris@16: // forward to helper Chris@16: return execute_helper::template execute(fsm,region_index,state,evt, Chris@16: ::boost::mpl::bool_< ::boost::mpl::empty::type::value>()); Chris@16: } Chris@16: }; Chris@16: // nullary metafunction whose only job is to prevent early evaluation of _1 Chris@16: template< typename Entry > Chris@16: struct make_chain_row_from_map_entry Chris@16: { Chris@16: // if we have more than one frow with the same state as source, remove the ones extra Chris@16: // note: we know the frow's are located at the beginning so we remove at the beginning (number of frows - 1) elements Chris@16: enum {number_frows = ::boost::mpl::count_if< typename Entry::second,has_is_frow< ::boost::mpl::placeholders::_1> >::value}; Chris@16: Chris@16: //erases the first NumberToDelete rows Chris@16: template Chris@16: struct erase_first_rows Chris@16: { Chris@16: typedef typename ::boost::mpl::erase< Chris@16: typename Entry::second, Chris@16: typename ::boost::mpl::begin::type, Chris@16: typename ::boost::mpl::advance< Chris@16: typename ::boost::mpl::begin::type, Chris@16: ::boost::mpl::int_ >::type Chris@16: >::type type; Chris@16: }; Chris@16: // if we have more than 1 frow with this event (not allowed), delete the spare Chris@16: typedef typename ::boost::mpl::eval_if< Chris@16: typename ::boost::mpl::bool_< number_frows >= 2 >::type, Chris@16: erase_first_rows, Chris@16: ::boost::mpl::identity Chris@16: >::type filtered_stt; Chris@16: Chris@16: typedef chain_row type; Chris@16: }; Chris@16: // helper for lazy evaluation in eval_if of change_frow_event Chris@16: template Chris@16: struct replace_event Chris@16: { Chris@16: typedef typename Transition::template replace_event::type type; Chris@16: }; Chris@16: // changes the event type for a frow to the event we are dispatching Chris@16: // this helps ensure that an event does not get processed more than once because of frows and base events. Chris@16: template Chris@16: struct change_frow_event Chris@16: { Chris@16: typedef typename ::boost::mpl::eval_if< Chris@16: typename has_is_frow::type, Chris@16: replace_event, Chris@16: boost::mpl::identity Chris@16: >::type type; Chris@16: }; Chris@16: // Compute the maximum state value in the sm so we know how big Chris@16: // to make the table Chris@16: typedef typename generate_state_set::type state_list; Chris@16: BOOST_STATIC_CONSTANT(int, max_state = ( ::boost::mpl::size::value)); Chris@16: Chris@16: template Chris@16: struct convert_event_and_forward Chris@16: { Chris@16: static HandledEnum execute(Fsm& fsm, int region_index, int state, Event const& evt) Chris@16: { Chris@16: typename Transition::transition_event forwarded(evt); Chris@16: return Transition::execute(fsm,region_index,state,forwarded); Chris@16: } Chris@16: }; Chris@16: Chris@16: // A function object for use with mpl::for_each that stuffs Chris@16: // transitions into cells. Chris@16: struct init_cell Chris@16: { Chris@16: init_cell(dispatch_table* self_) Chris@16: : self(self_) Chris@16: {} Chris@16: // version for transition event not base of our event Chris@16: // first for all transitions, then for internal ones of a fsm Chris@16: template Chris@16: typename ::boost::disable_if< Chris@16: typename ::boost::is_same::type Chris@16: ,void>::type Chris@16: init_event_base_case(Transition const&, ::boost::mpl::true_ const &, ::boost::mpl::false_ const &) const Chris@16: { Chris@16: typedef typename create_stt::type stt; Chris@16: BOOST_STATIC_CONSTANT(int, state_id = Chris@16: (get_state_id::value)); Chris@16: self->entries[state_id+1] = reinterpret_cast(&Transition::execute); Chris@16: } Chris@16: template Chris@16: typename ::boost::enable_if< Chris@16: typename ::boost::is_same::type Chris@16: ,void>::type Chris@16: init_event_base_case(Transition const&, ::boost::mpl::true_ const &, ::boost::mpl::false_ const &) const Chris@16: { Chris@16: self->entries[0] = reinterpret_cast(&Transition::execute); Chris@16: } Chris@16: Chris@16: // version for transition event is boost::any Chris@16: // first for all transitions, then for internal ones of a fsm Chris@16: template Chris@16: typename ::boost::disable_if< Chris@16: typename ::boost::is_same::type Chris@16: ,void>::type Chris@16: init_event_base_case(Transition const&, ::boost::mpl::false_ const &, ::boost::mpl::true_ const &) const Chris@16: { Chris@16: typedef typename create_stt::type stt; Chris@16: BOOST_STATIC_CONSTANT(int, state_id = Chris@16: (get_state_id::value)); Chris@16: self->entries[state_id+1] = &convert_event_and_forward::execute; Chris@16: } Chris@16: template Chris@16: typename ::boost::enable_if< Chris@16: typename ::boost::is_same::type Chris@16: ,void>::type Chris@16: init_event_base_case(Transition const&, ::boost::mpl::false_ const &, ::boost::mpl::true_ const &) const Chris@16: { Chris@16: self->entries[0] = &convert_event_and_forward::execute; Chris@16: } Chris@16: Chris@16: // version for transition event base of our event Chris@16: // first for all transitions, then for internal ones of a fsm Chris@16: template Chris@16: typename ::boost::disable_if< Chris@16: typename ::boost::is_same::type Chris@16: ,void>::type Chris@16: init_event_base_case(Transition const&, ::boost::mpl::false_ const &, ::boost::mpl::false_ const &) const Chris@16: { Chris@16: typedef typename create_stt::type stt; Chris@16: BOOST_STATIC_CONSTANT(int, state_id = Chris@16: (get_state_id::value)); Chris@16: self->entries[state_id+1] = &Transition::execute; Chris@16: } Chris@16: template Chris@16: typename ::boost::enable_if< Chris@16: typename ::boost::is_same::type Chris@16: ,void>::type Chris@16: init_event_base_case(Transition const&, ::boost::mpl::false_ const &, ::boost::mpl::false_ const &) const Chris@16: { Chris@16: self->entries[0] = &Transition::execute; Chris@16: } Chris@16: // Cell initializer function object, used with mpl::for_each Chris@16: template Chris@16: typename ::boost::enable_if::type,void >::type Chris@16: operator()(Transition const&,boost::msm::back::dummy<0> = 0) const Chris@16: { Chris@16: // version for not real rows. No problem because irrelevant for process_event Chris@16: } Chris@16: template Chris@16: typename ::boost::disable_if::type,void >::type Chris@16: operator()(Transition const& tr,boost::msm::back::dummy<1> = 0) const Chris@16: { Chris@16: //only if the transition event is a base of our event is the reinterpret_case safe Chris@16: init_event_base_case(tr, Chris@16: ::boost::mpl::bool_< Chris@16: ::boost::is_base_of::type::value>(), Chris@16: ::boost::mpl::bool_< Chris@16: ::boost::msm::is_kleene_event::type::value>()); Chris@16: } Chris@16: Chris@16: dispatch_table* self; Chris@16: }; Chris@16: Chris@16: // Cell default-initializer function object, used with mpl::for_each Chris@16: // initializes with call_no_transition, defer_transition or default_eventless_transition Chris@16: // variant for non-anonymous transitions Chris@16: template Chris@16: struct default_init_cell Chris@16: { Chris@16: default_init_cell(dispatch_table* self_,cell* tofill_entries_) Chris@16: : self(self_),tofill_entries(tofill_entries_) Chris@16: {} Chris@16: template Chris@16: typename ::boost::enable_if::type,void>::type Chris@16: operator()(boost::msm::wrap const&,boost::msm::back::dummy<0> = 0) Chris@16: { Chris@16: typedef typename create_stt::type stt; Chris@16: BOOST_STATIC_CONSTANT(int, state_id = (get_state_id::value)); Chris@16: cell call_no_transition = &Fsm::defer_transition; Chris@16: tofill_entries[state_id+1] = call_no_transition; Chris@16: } Chris@16: template Chris@16: typename ::boost::disable_if< Chris@16: typename ::boost::mpl::or_< Chris@16: typename has_state_delayed_event::type, Chris@16: typename ::boost::is_same::type Chris@16: >::type Chris@16: ,void >::type Chris@16: operator()(boost::msm::wrap const&,boost::msm::back::dummy<1> = 0) Chris@16: { Chris@16: typedef typename create_stt::type stt; Chris@16: BOOST_STATIC_CONSTANT(int, state_id = (get_state_id::value)); Chris@16: cell call_no_transition = &Fsm::call_no_transition; Chris@16: tofill_entries[state_id+1] = call_no_transition; Chris@16: } Chris@16: // case for internal transitions of this fsm Chris@16: template Chris@16: typename ::boost::enable_if< Chris@16: typename ::boost::mpl::and_< Chris@16: typename ::boost::mpl::not_::type>::type, Chris@16: typename ::boost::is_same::type Chris@16: >::type Chris@16: ,void>::type Chris@16: operator()(boost::msm::wrap const&,boost::msm::back::dummy<2> = 0) Chris@16: { Chris@16: cell call_no_transition = &Fsm::call_no_transition_internal; Chris@16: tofill_entries[0] = call_no_transition; Chris@16: } Chris@16: dispatch_table* self; Chris@16: cell* tofill_entries; Chris@16: }; Chris@16: Chris@16: // variant for anonymous transitions Chris@16: template Chris@16: struct default_init_cell::type>::type> Chris@16: { Chris@16: default_init_cell(dispatch_table* self_,cell* tofill_entries_) Chris@16: : self(self_),tofill_entries(tofill_entries_) Chris@16: {} Chris@16: Chris@16: // this event is a compound one (not a real one, just one for use in event-less transitions) Chris@16: // Note this event cannot be used as deferred! Chris@16: // case for internal transitions of this fsm Chris@16: template Chris@16: typename ::boost::disable_if< Chris@16: typename ::boost::is_same::type Chris@16: ,void>::type Chris@16: operator()(boost::msm::wrap const&,boost::msm::back::dummy<0> = 0) Chris@16: { Chris@16: typedef typename create_stt::type stt; Chris@16: BOOST_STATIC_CONSTANT(int, state_id = (get_state_id::value)); Chris@16: cell call_no_transition = &Fsm::default_eventless_transition; Chris@16: tofill_entries[state_id+1] = call_no_transition; Chris@16: } Chris@16: Chris@16: template Chris@16: typename ::boost::enable_if< Chris@16: typename ::boost::is_same::type Chris@16: ,void>::type Chris@16: operator()(boost::msm::wrap const&,boost::msm::back::dummy<1> = 0) Chris@16: { Chris@16: cell call_no_transition = &Fsm::default_eventless_transition; Chris@16: tofill_entries[0] = call_no_transition; Chris@16: } Chris@16: dispatch_table* self; Chris@16: cell* tofill_entries; Chris@16: }; Chris@16: Chris@16: public: Chris@16: // initialize the dispatch table for a given Event and Fsm Chris@16: dispatch_table() Chris@16: { Chris@16: // Initialize cells for no transition Chris@16: ::boost::mpl::for_each::type, Chris@16: boost::msm::wrap< ::boost::mpl::placeholders::_1> > Chris@16: (default_init_cell(this,entries)); Chris@16: Chris@16: // build chaining rows for rows coming from the same state and the current event Chris@16: // first we build a map of sequence for every source Chris@16: // in reverse order so that the frow's are handled first (UML priority) Chris@16: typedef typename ::boost::mpl::reverse_fold< Chris@16: // filter on event Chris@16: ::boost::mpl::filter_view Chris@16: , Event>, Chris@16: ::boost::msm::is_kleene_event > Chris@16: > Chris@16: >, Chris@16: // build a map Chris@16: ::boost::mpl::map<>, Chris@16: ::boost::mpl::if_< Chris@16: // if we already have a row on this source state Chris@16: ::boost::mpl::has_key< ::boost::mpl::placeholders::_1, Chris@16: transition_source_type< ::boost::mpl::placeholders::_2> >, Chris@16: // insert a new element in the value type Chris@16: ::boost::mpl::insert< Chris@16: ::boost::mpl::placeholders::_1, Chris@16: ::boost::mpl::pair, Chris@16: ::boost::mpl::push_back< Chris@16: ::boost::mpl::at< ::boost::mpl::placeholders::_1, Chris@16: transition_source_type< ::boost::mpl::placeholders::_2> >, Chris@16: change_frow_event< ::boost::mpl::placeholders::_2 > > Chris@16: > >, Chris@16: // first row on this source state, make a vector with 1 element Chris@16: ::boost::mpl::insert< Chris@16: ::boost::mpl::placeholders::_1, Chris@16: ::boost::mpl::pair, Chris@16: make_vector< change_frow_event< ::boost::mpl::placeholders::_2> > > > Chris@16: > Chris@16: >::type map_of_row_seq; Chris@16: // and then build chaining rows for all source states having more than 1 row Chris@16: typedef typename ::boost::mpl::fold< Chris@16: map_of_row_seq,::boost::mpl::vector0<>, Chris@16: ::boost::mpl::if_< Chris@16: ::boost::mpl::greater< ::boost::mpl::size< Chris@16: ::boost::mpl::second< ::boost::mpl::placeholders::_2> >, Chris@16: ::boost::mpl::int_<1> >, Chris@16: // we need row chaining Chris@16: ::boost::mpl::push_back< ::boost::mpl::placeholders::_1, Chris@16: make_chain_row_from_map_entry< ::boost::mpl::placeholders::_2> >, Chris@16: // just one row, no chaining, we rebuild the row like it was before Chris@16: ::boost::mpl::push_back< ::boost::mpl::placeholders::_1, Chris@16: get_first_element_pair_second< ::boost::mpl::placeholders::_2> > Chris@16: > >::type chained_rows; Chris@16: // Go back and fill in cells for matching transitions. Chris@16: ::boost::mpl::for_each(init_cell(this)); Chris@16: } Chris@16: Chris@16: // The singleton instance. Chris@16: static const dispatch_table instance; Chris@16: Chris@16: public: // data members Chris@16: // +1 => 0 is reserved for this fsm (internal transitions) Chris@16: cell entries[max_state+1]; Chris@16: }; Chris@16: Chris@16: }}} // boost::msm::back Chris@16: Chris@16: Chris@16: #endif //BOOST_MSM_BACK_DISPATCH_TABLE_H Chris@16: