Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-06-30 08:20:41

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