Back to home page

EIC code displayed by LXR

 
 

    


Warning, file /include/boost/msm/back11/dispatch_table.hpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

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