Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:41:57

0001 // Copyright 2008 Christophe Henry
0002 // henry UNDERSCORE christophe AT hotmail DOT com
0003 // This is an extended version of the state machine available in the boost::mpl library
0004 // Distributed under the same license as the original.
0005 // Copyright for the original version:
0006 // Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed
0007 // under the Boost Software License, Version 1.0. (See accompanying
0008 // file LICENSE_1_0.txt or copy at
0009 // http://www.boost.org/LICENSE_1_0.txt)
0010 
0011 #ifndef BOOST_MSM_BACK_DISPATCH_TABLE_H
0012 #define BOOST_MSM_BACK_DISPATCH_TABLE_H
0013 
0014 #include <utility>
0015 
0016 #include <boost/mpl/reverse_fold.hpp>
0017 #include <boost/mpl/greater.hpp>
0018 #include <boost/mpl/filter_view.hpp>
0019 #include <boost/mpl/pop_front.hpp>
0020 #include <boost/mpl/for_each.hpp>
0021 #include <boost/mpl/advance.hpp>
0022 
0023 #include <boost/type_traits/is_base_of.hpp>
0024 #include <boost/type_traits/is_same.hpp>
0025 
0026 #include <boost/msm/event_traits.hpp>
0027 #include <boost/msm/back/metafunctions.hpp>
0028 #include <boost/msm/back/common_types.hpp>
0029 
0030 BOOST_MPL_HAS_XXX_TRAIT_DEF(is_frow)
0031 
0032 namespace boost { namespace msm { namespace back 
0033 {
0034 
0035 // Generates a singleton runtime lookup table that maps current state
0036 // to a function that makes the SM take its transition on the given
0037 // Event type.
0038 template <class Fsm,class Stt, class Event,class CompilePolicy>
0039 struct dispatch_table
0040 {
0041  private:
0042     // This is a table of these function pointers.
0043     typedef HandledEnum (*cell)(Fsm&, int,int,Event const&);
0044     typedef bool (*guard)(Fsm&, Event const&);
0045 
0046     // class used to build a chain (or sequence) of transitions for a given event and start state
0047     // (like an UML diamond). Allows transition conflicts.
0048     template< typename Seq,typename AnEvent,typename State >
0049     struct chain_row
0050     {
0051         typedef State   current_state_type;
0052         typedef AnEvent transition_event;
0053 
0054         // helper for building a disable/enable_if-controlled execute function
0055         struct execute_helper
0056         {
0057             template <class Sequence>
0058             static
0059             HandledEnum
0060             execute(Fsm& , int, int, Event const& , ::boost::mpl::true_ const & )
0061             {
0062                 // if at least one guard rejected, this will be ignored, otherwise will generate an error
0063                 return HANDLED_FALSE;
0064             }
0065 
0066             template <class Sequence>
0067             static
0068             HandledEnum
0069             execute(Fsm& fsm, int region_index , int state, Event const& evt,
0070                     ::boost::mpl::false_ const & )
0071             {
0072                  // try the first guard
0073                  typedef typename ::boost::mpl::front<Sequence>::type first_row;
0074                  HandledEnum res = first_row::execute(fsm,region_index,state,evt);
0075                  if (HANDLED_TRUE!=res && HANDLED_DEFERRED!=res)
0076                  {
0077                     // if the first rejected, move on to the next one
0078                     HandledEnum sub_res = 
0079                          execute<typename ::boost::mpl::pop_front<Sequence>::type>(fsm,region_index,state,evt,
0080                             ::boost::mpl::bool_<
0081                                 ::boost::mpl::empty<typename ::boost::mpl::pop_front<Sequence>::type>::type::value>());
0082                     // if at least one guards rejects, the event will not generate a call to no_transition
0083                     if ((HANDLED_FALSE==sub_res) && (HANDLED_GUARD_REJECT==res) )
0084                         return HANDLED_GUARD_REJECT;
0085                     else
0086                         return sub_res;
0087                  }
0088                  return res;
0089             }
0090         };
0091         // Take the transition action and return the next state.
0092         static HandledEnum execute(Fsm& fsm, int region_index, int state, Event const& evt)
0093         {
0094             // forward to helper
0095             return execute_helper::template execute<Seq>(fsm,region_index,state,evt,
0096                 ::boost::mpl::bool_< ::boost::mpl::empty<Seq>::type::value>());
0097         }
0098     };
0099     // nullary metafunction whose only job is to prevent early evaluation of _1
0100     template< typename Entry > 
0101     struct make_chain_row_from_map_entry
0102     { 
0103         // if we have more than one frow with the same state as source, remove the ones extra
0104         // note: we know the frow's are located at the beginning so we remove at the beginning (number of frows - 1) elements
0105         enum {number_frows = ::boost::mpl::count_if< typename Entry::second,has_is_frow< ::boost::mpl::placeholders::_1> >::value};
0106 
0107         //erases the first NumberToDelete rows
0108         template<class Sequence, int NumberToDelete>
0109         struct erase_first_rows
0110         {
0111             typedef typename ::boost::mpl::erase<
0112                 typename Entry::second,
0113                 typename ::boost::mpl::begin<Sequence>::type,
0114                 typename ::boost::mpl::advance<
0115                         typename ::boost::mpl::begin<Sequence>::type, 
0116                         ::boost::mpl::int_<NumberToDelete> >::type
0117             >::type type;
0118         };
0119         // if we have more than 1 frow with this event (not allowed), delete the spare
0120         typedef typename ::boost::mpl::eval_if<
0121             typename ::boost::mpl::bool_< number_frows >= 2 >::type,
0122             erase_first_rows<typename Entry::second,number_frows-1>,
0123             ::boost::mpl::identity<typename Entry::second>
0124         >::type filtered_stt;
0125 
0126         typedef chain_row<filtered_stt,Event,
0127             typename Entry::first > type;
0128     }; 
0129     // helper for lazy evaluation in eval_if of change_frow_event
0130     template <class Transition,class NewEvent>
0131     struct replace_event
0132     {
0133         typedef typename Transition::template replace_event<NewEvent>::type type;
0134     };
0135     // changes the event type for a frow to the event we are dispatching
0136     // this helps ensure that an event does not get processed more than once because of frows and base events.
0137     template <class FrowTransition>
0138     struct change_frow_event
0139     {
0140         typedef typename ::boost::mpl::eval_if<
0141             typename has_is_frow<FrowTransition>::type,
0142             replace_event<FrowTransition,Event>,
0143             boost::mpl::identity<FrowTransition>
0144         >::type type;
0145     };
0146     // Compute the maximum state value in the sm so we know how big
0147     // to make the table
0148     typedef typename generate_state_set<Stt>::type state_list;
0149     BOOST_STATIC_CONSTANT(int, max_state = ( ::boost::mpl::size<state_list>::value));
0150 
0151     template <class Transition>
0152     struct convert_event_and_forward
0153     {
0154         static HandledEnum execute(Fsm& fsm, int region_index, int state, Event const& evt)
0155         {
0156             typename Transition::transition_event forwarded(evt);
0157             return Transition::execute(fsm,region_index,state,forwarded);
0158         }
0159     };
0160 
0161     // A function object for use with mpl::for_each that stuffs
0162     // transitions into cells.
0163     struct init_cell
0164     {
0165         init_cell(dispatch_table* self_)
0166           : self(self_)
0167         {}
0168         // version for transition event not base of our event
0169         // first for all transitions, then for internal ones of a fsm
0170         template <class Transition>
0171         typename ::boost::disable_if<
0172             typename ::boost::is_same<typename Transition::current_state_type,Fsm>::type
0173         ,void>::type
0174         init_event_base_case(Transition const&, ::boost::mpl::true_ const &, ::boost::mpl::false_ const &) const
0175         {
0176             typedef typename create_stt<Fsm>::type stt; 
0177             BOOST_STATIC_CONSTANT(int, state_id = 
0178                 (get_state_id<stt,typename Transition::current_state_type>::value));
0179             self->entries[state_id+1] = reinterpret_cast<cell>(&Transition::execute);
0180         }
0181         template <class Transition>
0182         typename ::boost::enable_if<
0183             typename ::boost::is_same<typename Transition::current_state_type,Fsm>::type
0184         ,void>::type
0185         init_event_base_case(Transition const&, ::boost::mpl::true_ const &, ::boost::mpl::false_ const &) const
0186         {
0187             self->entries[0] = reinterpret_cast<cell>(&Transition::execute);
0188         }
0189 
0190         // version for transition event is boost::any
0191         // first for all transitions, then for internal ones of a fsm
0192         template <class Transition>
0193         typename ::boost::disable_if<
0194             typename ::boost::is_same<typename Transition::current_state_type,Fsm>::type
0195         ,void>::type
0196         init_event_base_case(Transition const&, ::boost::mpl::false_ const &, ::boost::mpl::true_ const &) const
0197         {
0198             typedef typename create_stt<Fsm>::type stt; 
0199             BOOST_STATIC_CONSTANT(int, state_id = 
0200                 (get_state_id<stt,typename Transition::current_state_type>::value));
0201             self->entries[state_id+1] = &convert_event_and_forward<Transition>::execute;
0202         }
0203         template <class Transition>
0204         typename ::boost::enable_if<
0205             typename ::boost::is_same<typename Transition::current_state_type,Fsm>::type
0206         ,void>::type
0207         init_event_base_case(Transition const&, ::boost::mpl::false_ const &, ::boost::mpl::true_ const &) const
0208         {
0209             self->entries[0] = &convert_event_and_forward<Transition>::execute;
0210         }
0211         template <class Transition>
0212         typename ::boost::disable_if<
0213             typename ::boost::is_same<typename Transition::current_state_type,Fsm>::type
0214         ,void>::type
0215         init_event_base_case(Transition const&, ::boost::mpl::true_ const &, ::boost::mpl::true_ const &) const
0216         {
0217             typedef typename create_stt<Fsm>::type stt;
0218             BOOST_STATIC_CONSTANT(int, state_id =
0219                 (get_state_id<stt,typename Transition::current_state_type>::value));
0220             self->entries[state_id+1] = &convert_event_and_forward<Transition>::execute;
0221         }
0222         template <class Transition>
0223         typename ::boost::enable_if<
0224             typename ::boost::is_same<typename Transition::current_state_type,Fsm>::type
0225         ,void>::type
0226         init_event_base_case(Transition const&, ::boost::mpl::true_ const &, ::boost::mpl::true_ const &) const
0227         {
0228             self->entries[0] = &convert_event_and_forward<Transition>::execute;
0229         }
0230         // end version for kleene
0231 
0232         // version for transition event base of our event
0233         // first for all transitions, then for internal ones of a fsm
0234         template <class Transition>
0235         typename ::boost::disable_if<
0236             typename ::boost::is_same<typename Transition::current_state_type,Fsm>::type
0237         ,void>::type
0238         init_event_base_case(Transition const&, ::boost::mpl::false_ const &, ::boost::mpl::false_ const &) const
0239         {
0240             typedef typename create_stt<Fsm>::type stt; 
0241             BOOST_STATIC_CONSTANT(int, state_id = 
0242                 (get_state_id<stt,typename Transition::current_state_type>::value));
0243             self->entries[state_id+1] = &Transition::execute;
0244         }
0245         template <class Transition>
0246         typename ::boost::enable_if<
0247             typename ::boost::is_same<typename Transition::current_state_type,Fsm>::type
0248         ,void>::type
0249         init_event_base_case(Transition const&, ::boost::mpl::false_ const &, ::boost::mpl::false_ const &) const
0250         {
0251             self->entries[0] = &Transition::execute;
0252         }
0253         // Cell initializer function object, used with mpl::for_each
0254         template <class Transition>
0255         typename ::boost::enable_if<typename has_not_real_row_tag<Transition>::type,void >::type
0256             operator()(Transition const&,boost::msm::back::dummy<0> = 0) const
0257         {
0258             // version for not real rows. No problem because irrelevant for process_event
0259         }
0260         template <class Transition>
0261         typename ::boost::disable_if<typename has_not_real_row_tag<Transition>::type,void >::type
0262         operator()(Transition const& tr,boost::msm::back::dummy<1> = 0) const
0263         {
0264             //only if the transition event is a base of our event is the reinterpret_case safe
0265             init_event_base_case(tr,
0266                 ::boost::mpl::bool_< 
0267                     ::boost::is_base_of<typename Transition::transition_event,Event>::type::value>(),
0268                 ::boost::mpl::bool_< 
0269                     ::boost::msm::is_kleene_event<typename Transition::transition_event>::type::value>());
0270         }
0271     
0272         dispatch_table* self;
0273     };
0274 
0275     // Cell default-initializer function object, used with mpl::for_each
0276     // initializes with call_no_transition, defer_transition or default_eventless_transition
0277     // variant for non-anonymous transitions
0278     template <class EventType,class Enable=void>
0279     struct default_init_cell
0280     {
0281         default_init_cell(dispatch_table* self_,cell* tofill_entries_)
0282             : self(self_),tofill_entries(tofill_entries_)
0283         {}
0284         template <class State>
0285         typename ::boost::enable_if<typename has_state_delayed_event<State,Event>::type,void>::type
0286         operator()(boost::msm::wrap<State> const&,boost::msm::back::dummy<0> = 0)
0287         {
0288             typedef typename create_stt<Fsm>::type stt; 
0289             BOOST_STATIC_CONSTANT(int, state_id = (get_state_id<stt,State>::value));
0290             cell call_no_transition = &Fsm::defer_transition;
0291             tofill_entries[state_id+1] = call_no_transition;
0292         }
0293         template <class State>
0294         typename ::boost::disable_if<
0295             typename ::boost::mpl::or_<
0296                 typename has_state_delayed_event<State,Event>::type,
0297                 typename ::boost::is_same<State,Fsm>::type
0298             >::type
0299         ,void >::type
0300         operator()(boost::msm::wrap<State> const&,boost::msm::back::dummy<1> = 0)
0301         {
0302             typedef typename create_stt<Fsm>::type stt; 
0303             BOOST_STATIC_CONSTANT(int, state_id = (get_state_id<stt,State>::value));
0304             cell call_no_transition = &Fsm::call_no_transition;
0305             tofill_entries[state_id+1] = call_no_transition;
0306         }
0307         // case for internal transitions of this fsm
0308         template <class State>
0309         typename ::boost::enable_if<
0310             typename ::boost::mpl::and_<
0311                 typename ::boost::mpl::not_<typename has_state_delayed_event<State,Event>::type>::type,
0312                 typename ::boost::is_same<State,Fsm>::type
0313             >::type
0314         ,void>::type
0315         operator()(boost::msm::wrap<State> const&,boost::msm::back::dummy<2> = 0)
0316         {
0317             cell call_no_transition = &Fsm::call_no_transition_internal;
0318             tofill_entries[0] = call_no_transition;
0319         }
0320         dispatch_table* self;
0321         cell* tofill_entries;
0322     };
0323 
0324     // variant for anonymous transitions
0325     template <class EventType>
0326     struct default_init_cell<EventType,
0327                              typename ::boost::enable_if<
0328                                 typename is_completion_event<EventType>::type>::type>
0329     {
0330         default_init_cell(dispatch_table* self_,cell* tofill_entries_)
0331             : self(self_),tofill_entries(tofill_entries_)
0332         {}
0333 
0334         // this event is a compound one (not a real one, just one for use in event-less transitions)
0335         // Note this event cannot be used as deferred!
0336         // case for internal transitions of this fsm 
0337         template <class State>
0338         typename ::boost::disable_if<
0339             typename ::boost::is_same<State,Fsm>::type
0340         ,void>::type
0341         operator()(boost::msm::wrap<State> const&,boost::msm::back::dummy<0> = 0)
0342         {
0343             typedef typename create_stt<Fsm>::type stt; 
0344             BOOST_STATIC_CONSTANT(int, state_id = (get_state_id<stt,State>::value));
0345             cell call_no_transition = &Fsm::default_eventless_transition;
0346             tofill_entries[state_id+1] = call_no_transition;
0347         }
0348 
0349         template <class State>
0350         typename ::boost::enable_if<
0351             typename ::boost::is_same<State,Fsm>::type
0352         ,void>::type
0353         operator()(boost::msm::wrap<State> const&,boost::msm::back::dummy<1> = 0)
0354         {
0355             cell call_no_transition = &Fsm::default_eventless_transition;
0356             tofill_entries[0] = call_no_transition;
0357         }
0358         dispatch_table* self;
0359         cell* tofill_entries;
0360     };
0361 
0362  public:
0363     // initialize the dispatch table for a given Event and Fsm
0364     dispatch_table()
0365     {
0366         // Initialize cells for no transition
0367         ::boost::mpl::for_each<typename generate_state_set<Stt>::type, 
0368                                boost::msm::wrap< ::boost::mpl::placeholders::_1> >
0369                         (default_init_cell<Event>(this,entries));
0370 
0371         // build chaining rows for rows coming from the same state and the current event
0372         // first we build a map of sequence for every source
0373         // in reverse order so that the frow's are handled first (UML priority)
0374         typedef typename ::boost::mpl::reverse_fold<
0375                         // filter on event
0376                         ::boost::mpl::filter_view
0377                             <Stt, boost::mpl::or_<
0378                                     ::boost::is_base_of<transition_event< ::boost::mpl::placeholders::_>, Event>,
0379                                     ::boost::msm::is_kleene_event<transition_event< ::boost::mpl::placeholders::_> >
0380                                     >
0381                             >,
0382                         // build a map
0383                         ::boost::mpl::map<>,
0384                         ::boost::mpl::if_<
0385                             // if we already have a row on this source state
0386                             ::boost::mpl::has_key< ::boost::mpl::placeholders::_1,
0387                                                    transition_source_type< ::boost::mpl::placeholders::_2> >,
0388                             // insert a new element in the value type
0389                             ::boost::mpl::insert< 
0390                                 ::boost::mpl::placeholders::_1,
0391                                 ::boost::mpl::pair<transition_source_type< ::boost::mpl::placeholders::_2>,
0392                                                    ::boost::mpl::push_back< 
0393                                                         ::boost::mpl::at< ::boost::mpl::placeholders::_1,
0394                                                         transition_source_type< ::boost::mpl::placeholders::_2> >,
0395                                                         change_frow_event< ::boost::mpl::placeholders::_2 > > 
0396                                                    > >,
0397                             // first row on this source state, make a vector with 1 element
0398                             ::boost::mpl::insert< 
0399                                         ::boost::mpl::placeholders::_1,
0400                                         ::boost::mpl::pair<transition_source_type< ::boost::mpl::placeholders::_2>,
0401                                         make_vector< change_frow_event< ::boost::mpl::placeholders::_2> > > >
0402                                >
0403                        >::type map_of_row_seq;
0404         // and then build chaining rows for all source states having more than 1 row
0405         typedef typename ::boost::mpl::fold<
0406             map_of_row_seq,::boost::mpl::vector0<>,
0407             ::boost::mpl::if_<
0408                      ::boost::mpl::greater< ::boost::mpl::size< 
0409                                                     ::boost::mpl::second< ::boost::mpl::placeholders::_2> >,
0410                                             ::boost::mpl::int_<1> >,
0411                      // we need row chaining
0412                      ::boost::mpl::push_back< ::boost::mpl::placeholders::_1, 
0413                                     make_chain_row_from_map_entry< ::boost::mpl::placeholders::_2> >,
0414                      // just one row, no chaining, we rebuild the row like it was before
0415                      ::boost::mpl::push_back< ::boost::mpl::placeholders::_1, 
0416                                               get_first_element_pair_second< ::boost::mpl::placeholders::_2> > 
0417              > >::type chained_rows; 
0418         // Go back and fill in cells for matching transitions.
0419         ::boost::mpl::for_each<chained_rows>(init_cell(this));
0420     }
0421 
0422     // The singleton instance.
0423     static const dispatch_table& instance() {
0424         static dispatch_table table;
0425         return table;
0426     }
0427 
0428  public: // data members
0429      // +1 => 0 is reserved for this fsm (internal transitions)
0430     cell entries[max_state+1];
0431 };
0432 
0433 }}} // boost::msm::back
0434 
0435 
0436 #endif //BOOST_MSM_BACK_DISPATCH_TABLE_H
0437