annotate DEPENDENCIES/generic/include/boost/msm/back/dispatch_table.hpp @ 133:4acb5d8d80b6 tip

Don't fail environmental check if README.md exists (but .txt and no-suffix don't)
author Chris Cannam
date Tue, 30 Jul 2019 12:25:44 +0100
parents 2665513ce2d3
children
rev   line source
Chris@16 1 // Copyright 2008 Christophe Henry
Chris@16 2 // henry UNDERSCORE christophe AT hotmail DOT com
Chris@16 3 // This is an extended version of the state machine available in the boost::mpl library
Chris@16 4 // Distributed under the same license as the original.
Chris@16 5 // Copyright for the original version:
Chris@16 6 // Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed
Chris@16 7 // under the Boost Software License, Version 1.0. (See accompanying
Chris@16 8 // file LICENSE_1_0.txt or copy at
Chris@16 9 // http://www.boost.org/LICENSE_1_0.txt)
Chris@16 10
Chris@16 11 #ifndef BOOST_MSM_BACK_DISPATCH_TABLE_H
Chris@16 12 #define BOOST_MSM_BACK_DISPATCH_TABLE_H
Chris@16 13
Chris@16 14 #include <utility>
Chris@16 15
Chris@16 16 #include <boost/mpl/reverse_fold.hpp>
Chris@16 17 #include <boost/mpl/greater.hpp>
Chris@16 18 #include <boost/mpl/filter_view.hpp>
Chris@16 19 #include <boost/mpl/pop_front.hpp>
Chris@16 20 #include <boost/mpl/for_each.hpp>
Chris@16 21 #include <boost/mpl/advance.hpp>
Chris@16 22
Chris@16 23 #include <boost/type_traits/is_base_of.hpp>
Chris@16 24 #include <boost/type_traits/is_same.hpp>
Chris@16 25
Chris@16 26 #include <boost/msm/event_traits.hpp>
Chris@16 27 #include <boost/msm/back/metafunctions.hpp>
Chris@16 28 #include <boost/msm/back/common_types.hpp>
Chris@16 29
Chris@16 30 BOOST_MPL_HAS_XXX_TRAIT_DEF(is_frow)
Chris@16 31
Chris@16 32 namespace boost { namespace msm { namespace back
Chris@16 33 {
Chris@16 34
Chris@16 35 // Generates a singleton runtime lookup table that maps current state
Chris@16 36 // to a function that makes the SM take its transition on the given
Chris@16 37 // Event type.
Chris@16 38 template <class Fsm,class Stt, class Event,class CompilePolicy>
Chris@16 39 struct dispatch_table
Chris@16 40 {
Chris@16 41 private:
Chris@16 42 // This is a table of these function pointers.
Chris@16 43 typedef HandledEnum (*cell)(Fsm&, int,int,Event const&);
Chris@16 44 typedef bool (*guard)(Fsm&, Event const&);
Chris@16 45
Chris@16 46 // class used to build a chain (or sequence) of transitions for a given event and start state
Chris@16 47 // (like an UML diamond). Allows transition conflicts.
Chris@16 48 template< typename Seq,typename AnEvent,typename State >
Chris@16 49 struct chain_row
Chris@16 50 {
Chris@16 51 typedef State current_state_type;
Chris@16 52 typedef AnEvent transition_event;
Chris@16 53
Chris@16 54 // helper for building a disable/enable_if-controlled execute function
Chris@16 55 struct execute_helper
Chris@16 56 {
Chris@16 57 template <class Sequence>
Chris@16 58 static
Chris@16 59 HandledEnum
Chris@16 60 execute(Fsm& , int, int, Event const& , ::boost::mpl::true_ const & )
Chris@16 61 {
Chris@16 62 // if at least one guard rejected, this will be ignored, otherwise will generate an error
Chris@16 63 return HANDLED_FALSE;
Chris@16 64 }
Chris@16 65
Chris@16 66 template <class Sequence>
Chris@16 67 static
Chris@16 68 HandledEnum
Chris@16 69 execute(Fsm& fsm, int region_index , int state, Event const& evt,
Chris@16 70 ::boost::mpl::false_ const & )
Chris@16 71 {
Chris@16 72 // try the first guard
Chris@16 73 typedef typename ::boost::mpl::front<Sequence>::type first_row;
Chris@16 74 HandledEnum res = first_row::execute(fsm,region_index,state,evt);
Chris@16 75 if (HANDLED_TRUE!=res && HANDLED_DEFERRED!=res)
Chris@16 76 {
Chris@16 77 // if the first rejected, move on to the next one
Chris@16 78 HandledEnum sub_res =
Chris@16 79 execute<typename ::boost::mpl::pop_front<Sequence>::type>(fsm,region_index,state,evt,
Chris@16 80 ::boost::mpl::bool_<
Chris@16 81 ::boost::mpl::empty<typename ::boost::mpl::pop_front<Sequence>::type>::type::value>());
Chris@16 82 // if at least one guards rejects, the event will not generate a call to no_transition
Chris@16 83 if ((HANDLED_FALSE==sub_res) && (HANDLED_GUARD_REJECT==res) )
Chris@16 84 return HANDLED_GUARD_REJECT;
Chris@16 85 else
Chris@16 86 return sub_res;
Chris@16 87 }
Chris@16 88 return res;
Chris@16 89 }
Chris@16 90 };
Chris@16 91 // Take the transition action and return the next state.
Chris@16 92 static HandledEnum execute(Fsm& fsm, int region_index, int state, Event const& evt)
Chris@16 93 {
Chris@16 94 // forward to helper
Chris@16 95 return execute_helper::template execute<Seq>(fsm,region_index,state,evt,
Chris@16 96 ::boost::mpl::bool_< ::boost::mpl::empty<Seq>::type::value>());
Chris@16 97 }
Chris@16 98 };
Chris@16 99 // nullary metafunction whose only job is to prevent early evaluation of _1
Chris@16 100 template< typename Entry >
Chris@16 101 struct make_chain_row_from_map_entry
Chris@16 102 {
Chris@16 103 // if we have more than one frow with the same state as source, remove the ones extra
Chris@16 104 // note: we know the frow's are located at the beginning so we remove at the beginning (number of frows - 1) elements
Chris@16 105 enum {number_frows = ::boost::mpl::count_if< typename Entry::second,has_is_frow< ::boost::mpl::placeholders::_1> >::value};
Chris@16 106
Chris@16 107 //erases the first NumberToDelete rows
Chris@16 108 template<class Sequence, int NumberToDelete>
Chris@16 109 struct erase_first_rows
Chris@16 110 {
Chris@16 111 typedef typename ::boost::mpl::erase<
Chris@16 112 typename Entry::second,
Chris@16 113 typename ::boost::mpl::begin<Sequence>::type,
Chris@16 114 typename ::boost::mpl::advance<
Chris@16 115 typename ::boost::mpl::begin<Sequence>::type,
Chris@16 116 ::boost::mpl::int_<NumberToDelete> >::type
Chris@16 117 >::type type;
Chris@16 118 };
Chris@16 119 // if we have more than 1 frow with this event (not allowed), delete the spare
Chris@16 120 typedef typename ::boost::mpl::eval_if<
Chris@16 121 typename ::boost::mpl::bool_< number_frows >= 2 >::type,
Chris@16 122 erase_first_rows<typename Entry::second,number_frows-1>,
Chris@16 123 ::boost::mpl::identity<typename Entry::second>
Chris@16 124 >::type filtered_stt;
Chris@16 125
Chris@16 126 typedef chain_row<filtered_stt,Event,
Chris@16 127 typename Entry::first > type;
Chris@16 128 };
Chris@16 129 // helper for lazy evaluation in eval_if of change_frow_event
Chris@16 130 template <class Transition,class NewEvent>
Chris@16 131 struct replace_event
Chris@16 132 {
Chris@16 133 typedef typename Transition::template replace_event<NewEvent>::type type;
Chris@16 134 };
Chris@16 135 // changes the event type for a frow to the event we are dispatching
Chris@16 136 // this helps ensure that an event does not get processed more than once because of frows and base events.
Chris@16 137 template <class FrowTransition>
Chris@16 138 struct change_frow_event
Chris@16 139 {
Chris@16 140 typedef typename ::boost::mpl::eval_if<
Chris@16 141 typename has_is_frow<FrowTransition>::type,
Chris@16 142 replace_event<FrowTransition,Event>,
Chris@16 143 boost::mpl::identity<FrowTransition>
Chris@16 144 >::type type;
Chris@16 145 };
Chris@16 146 // Compute the maximum state value in the sm so we know how big
Chris@16 147 // to make the table
Chris@16 148 typedef typename generate_state_set<Stt>::type state_list;
Chris@16 149 BOOST_STATIC_CONSTANT(int, max_state = ( ::boost::mpl::size<state_list>::value));
Chris@16 150
Chris@16 151 template <class Transition>
Chris@16 152 struct convert_event_and_forward
Chris@16 153 {
Chris@16 154 static HandledEnum execute(Fsm& fsm, int region_index, int state, Event const& evt)
Chris@16 155 {
Chris@16 156 typename Transition::transition_event forwarded(evt);
Chris@16 157 return Transition::execute(fsm,region_index,state,forwarded);
Chris@16 158 }
Chris@16 159 };
Chris@16 160
Chris@16 161 // A function object for use with mpl::for_each that stuffs
Chris@16 162 // transitions into cells.
Chris@16 163 struct init_cell
Chris@16 164 {
Chris@16 165 init_cell(dispatch_table* self_)
Chris@16 166 : self(self_)
Chris@16 167 {}
Chris@16 168 // version for transition event not base of our event
Chris@16 169 // first for all transitions, then for internal ones of a fsm
Chris@16 170 template <class Transition>
Chris@16 171 typename ::boost::disable_if<
Chris@16 172 typename ::boost::is_same<typename Transition::current_state_type,Fsm>::type
Chris@16 173 ,void>::type
Chris@16 174 init_event_base_case(Transition const&, ::boost::mpl::true_ const &, ::boost::mpl::false_ const &) const
Chris@16 175 {
Chris@16 176 typedef typename create_stt<Fsm>::type stt;
Chris@16 177 BOOST_STATIC_CONSTANT(int, state_id =
Chris@16 178 (get_state_id<stt,typename Transition::current_state_type>::value));
Chris@16 179 self->entries[state_id+1] = reinterpret_cast<cell>(&Transition::execute);
Chris@16 180 }
Chris@16 181 template <class Transition>
Chris@16 182 typename ::boost::enable_if<
Chris@16 183 typename ::boost::is_same<typename Transition::current_state_type,Fsm>::type
Chris@16 184 ,void>::type
Chris@16 185 init_event_base_case(Transition const&, ::boost::mpl::true_ const &, ::boost::mpl::false_ const &) const
Chris@16 186 {
Chris@16 187 self->entries[0] = reinterpret_cast<cell>(&Transition::execute);
Chris@16 188 }
Chris@16 189
Chris@16 190 // version for transition event is boost::any
Chris@16 191 // first for all transitions, then for internal ones of a fsm
Chris@16 192 template <class Transition>
Chris@16 193 typename ::boost::disable_if<
Chris@16 194 typename ::boost::is_same<typename Transition::current_state_type,Fsm>::type
Chris@16 195 ,void>::type
Chris@16 196 init_event_base_case(Transition const&, ::boost::mpl::false_ const &, ::boost::mpl::true_ const &) const
Chris@16 197 {
Chris@16 198 typedef typename create_stt<Fsm>::type stt;
Chris@16 199 BOOST_STATIC_CONSTANT(int, state_id =
Chris@16 200 (get_state_id<stt,typename Transition::current_state_type>::value));
Chris@16 201 self->entries[state_id+1] = &convert_event_and_forward<Transition>::execute;
Chris@16 202 }
Chris@16 203 template <class Transition>
Chris@16 204 typename ::boost::enable_if<
Chris@16 205 typename ::boost::is_same<typename Transition::current_state_type,Fsm>::type
Chris@16 206 ,void>::type
Chris@16 207 init_event_base_case(Transition const&, ::boost::mpl::false_ const &, ::boost::mpl::true_ const &) const
Chris@16 208 {
Chris@16 209 self->entries[0] = &convert_event_and_forward<Transition>::execute;
Chris@16 210 }
Chris@16 211
Chris@16 212 // version for transition event base of our event
Chris@16 213 // first for all transitions, then for internal ones of a fsm
Chris@16 214 template <class Transition>
Chris@16 215 typename ::boost::disable_if<
Chris@16 216 typename ::boost::is_same<typename Transition::current_state_type,Fsm>::type
Chris@16 217 ,void>::type
Chris@16 218 init_event_base_case(Transition const&, ::boost::mpl::false_ const &, ::boost::mpl::false_ const &) const
Chris@16 219 {
Chris@16 220 typedef typename create_stt<Fsm>::type stt;
Chris@16 221 BOOST_STATIC_CONSTANT(int, state_id =
Chris@16 222 (get_state_id<stt,typename Transition::current_state_type>::value));
Chris@16 223 self->entries[state_id+1] = &Transition::execute;
Chris@16 224 }
Chris@16 225 template <class Transition>
Chris@16 226 typename ::boost::enable_if<
Chris@16 227 typename ::boost::is_same<typename Transition::current_state_type,Fsm>::type
Chris@16 228 ,void>::type
Chris@16 229 init_event_base_case(Transition const&, ::boost::mpl::false_ const &, ::boost::mpl::false_ const &) const
Chris@16 230 {
Chris@16 231 self->entries[0] = &Transition::execute;
Chris@16 232 }
Chris@16 233 // Cell initializer function object, used with mpl::for_each
Chris@16 234 template <class Transition>
Chris@16 235 typename ::boost::enable_if<typename has_not_real_row_tag<Transition>::type,void >::type
Chris@16 236 operator()(Transition const&,boost::msm::back::dummy<0> = 0) const
Chris@16 237 {
Chris@16 238 // version for not real rows. No problem because irrelevant for process_event
Chris@16 239 }
Chris@16 240 template <class Transition>
Chris@16 241 typename ::boost::disable_if<typename has_not_real_row_tag<Transition>::type,void >::type
Chris@16 242 operator()(Transition const& tr,boost::msm::back::dummy<1> = 0) const
Chris@16 243 {
Chris@16 244 //only if the transition event is a base of our event is the reinterpret_case safe
Chris@16 245 init_event_base_case(tr,
Chris@16 246 ::boost::mpl::bool_<
Chris@16 247 ::boost::is_base_of<typename Transition::transition_event,Event>::type::value>(),
Chris@16 248 ::boost::mpl::bool_<
Chris@16 249 ::boost::msm::is_kleene_event<typename Transition::transition_event>::type::value>());
Chris@16 250 }
Chris@16 251
Chris@16 252 dispatch_table* self;
Chris@16 253 };
Chris@16 254
Chris@16 255 // Cell default-initializer function object, used with mpl::for_each
Chris@16 256 // initializes with call_no_transition, defer_transition or default_eventless_transition
Chris@16 257 // variant for non-anonymous transitions
Chris@16 258 template <class EventType,class Enable=void>
Chris@16 259 struct default_init_cell
Chris@16 260 {
Chris@16 261 default_init_cell(dispatch_table* self_,cell* tofill_entries_)
Chris@16 262 : self(self_),tofill_entries(tofill_entries_)
Chris@16 263 {}
Chris@16 264 template <class State>
Chris@16 265 typename ::boost::enable_if<typename has_state_delayed_event<State,Event>::type,void>::type
Chris@16 266 operator()(boost::msm::wrap<State> const&,boost::msm::back::dummy<0> = 0)
Chris@16 267 {
Chris@16 268 typedef typename create_stt<Fsm>::type stt;
Chris@16 269 BOOST_STATIC_CONSTANT(int, state_id = (get_state_id<stt,State>::value));
Chris@16 270 cell call_no_transition = &Fsm::defer_transition;
Chris@16 271 tofill_entries[state_id+1] = call_no_transition;
Chris@16 272 }
Chris@16 273 template <class State>
Chris@16 274 typename ::boost::disable_if<
Chris@16 275 typename ::boost::mpl::or_<
Chris@16 276 typename has_state_delayed_event<State,Event>::type,
Chris@16 277 typename ::boost::is_same<State,Fsm>::type
Chris@16 278 >::type
Chris@16 279 ,void >::type
Chris@16 280 operator()(boost::msm::wrap<State> const&,boost::msm::back::dummy<1> = 0)
Chris@16 281 {
Chris@16 282 typedef typename create_stt<Fsm>::type stt;
Chris@16 283 BOOST_STATIC_CONSTANT(int, state_id = (get_state_id<stt,State>::value));
Chris@16 284 cell call_no_transition = &Fsm::call_no_transition;
Chris@16 285 tofill_entries[state_id+1] = call_no_transition;
Chris@16 286 }
Chris@16 287 // case for internal transitions of this fsm
Chris@16 288 template <class State>
Chris@16 289 typename ::boost::enable_if<
Chris@16 290 typename ::boost::mpl::and_<
Chris@16 291 typename ::boost::mpl::not_<typename has_state_delayed_event<State,Event>::type>::type,
Chris@16 292 typename ::boost::is_same<State,Fsm>::type
Chris@16 293 >::type
Chris@16 294 ,void>::type
Chris@16 295 operator()(boost::msm::wrap<State> const&,boost::msm::back::dummy<2> = 0)
Chris@16 296 {
Chris@16 297 cell call_no_transition = &Fsm::call_no_transition_internal;
Chris@16 298 tofill_entries[0] = call_no_transition;
Chris@16 299 }
Chris@16 300 dispatch_table* self;
Chris@16 301 cell* tofill_entries;
Chris@16 302 };
Chris@16 303
Chris@16 304 // variant for anonymous transitions
Chris@16 305 template <class EventType>
Chris@16 306 struct default_init_cell<EventType,
Chris@16 307 typename ::boost::enable_if<
Chris@16 308 typename is_completion_event<EventType>::type>::type>
Chris@16 309 {
Chris@16 310 default_init_cell(dispatch_table* self_,cell* tofill_entries_)
Chris@16 311 : self(self_),tofill_entries(tofill_entries_)
Chris@16 312 {}
Chris@16 313
Chris@16 314 // this event is a compound one (not a real one, just one for use in event-less transitions)
Chris@16 315 // Note this event cannot be used as deferred!
Chris@16 316 // case for internal transitions of this fsm
Chris@16 317 template <class State>
Chris@16 318 typename ::boost::disable_if<
Chris@16 319 typename ::boost::is_same<State,Fsm>::type
Chris@16 320 ,void>::type
Chris@16 321 operator()(boost::msm::wrap<State> const&,boost::msm::back::dummy<0> = 0)
Chris@16 322 {
Chris@16 323 typedef typename create_stt<Fsm>::type stt;
Chris@16 324 BOOST_STATIC_CONSTANT(int, state_id = (get_state_id<stt,State>::value));
Chris@16 325 cell call_no_transition = &Fsm::default_eventless_transition;
Chris@16 326 tofill_entries[state_id+1] = call_no_transition;
Chris@16 327 }
Chris@16 328
Chris@16 329 template <class State>
Chris@16 330 typename ::boost::enable_if<
Chris@16 331 typename ::boost::is_same<State,Fsm>::type
Chris@16 332 ,void>::type
Chris@16 333 operator()(boost::msm::wrap<State> const&,boost::msm::back::dummy<1> = 0)
Chris@16 334 {
Chris@16 335 cell call_no_transition = &Fsm::default_eventless_transition;
Chris@16 336 tofill_entries[0] = call_no_transition;
Chris@16 337 }
Chris@16 338 dispatch_table* self;
Chris@16 339 cell* tofill_entries;
Chris@16 340 };
Chris@16 341
Chris@16 342 public:
Chris@16 343 // initialize the dispatch table for a given Event and Fsm
Chris@16 344 dispatch_table()
Chris@16 345 {
Chris@16 346 // Initialize cells for no transition
Chris@16 347 ::boost::mpl::for_each<typename generate_state_set<Stt>::type,
Chris@16 348 boost::msm::wrap< ::boost::mpl::placeholders::_1> >
Chris@16 349 (default_init_cell<Event>(this,entries));
Chris@16 350
Chris@16 351 // build chaining rows for rows coming from the same state and the current event
Chris@16 352 // first we build a map of sequence for every source
Chris@16 353 // in reverse order so that the frow's are handled first (UML priority)
Chris@16 354 typedef typename ::boost::mpl::reverse_fold<
Chris@16 355 // filter on event
Chris@16 356 ::boost::mpl::filter_view
Chris@16 357 <Stt, boost::mpl::or_<
Chris@16 358 ::boost::is_base_of<transition_event< ::boost::mpl::placeholders::_>, Event>,
Chris@16 359 ::boost::msm::is_kleene_event<transition_event< ::boost::mpl::placeholders::_> >
Chris@16 360 >
Chris@16 361 >,
Chris@16 362 // build a map
Chris@16 363 ::boost::mpl::map<>,
Chris@16 364 ::boost::mpl::if_<
Chris@16 365 // if we already have a row on this source state
Chris@16 366 ::boost::mpl::has_key< ::boost::mpl::placeholders::_1,
Chris@16 367 transition_source_type< ::boost::mpl::placeholders::_2> >,
Chris@16 368 // insert a new element in the value type
Chris@16 369 ::boost::mpl::insert<
Chris@16 370 ::boost::mpl::placeholders::_1,
Chris@16 371 ::boost::mpl::pair<transition_source_type< ::boost::mpl::placeholders::_2>,
Chris@16 372 ::boost::mpl::push_back<
Chris@16 373 ::boost::mpl::at< ::boost::mpl::placeholders::_1,
Chris@16 374 transition_source_type< ::boost::mpl::placeholders::_2> >,
Chris@16 375 change_frow_event< ::boost::mpl::placeholders::_2 > >
Chris@16 376 > >,
Chris@16 377 // first row on this source state, make a vector with 1 element
Chris@16 378 ::boost::mpl::insert<
Chris@16 379 ::boost::mpl::placeholders::_1,
Chris@16 380 ::boost::mpl::pair<transition_source_type< ::boost::mpl::placeholders::_2>,
Chris@16 381 make_vector< change_frow_event< ::boost::mpl::placeholders::_2> > > >
Chris@16 382 >
Chris@16 383 >::type map_of_row_seq;
Chris@16 384 // and then build chaining rows for all source states having more than 1 row
Chris@16 385 typedef typename ::boost::mpl::fold<
Chris@16 386 map_of_row_seq,::boost::mpl::vector0<>,
Chris@16 387 ::boost::mpl::if_<
Chris@16 388 ::boost::mpl::greater< ::boost::mpl::size<
Chris@16 389 ::boost::mpl::second< ::boost::mpl::placeholders::_2> >,
Chris@16 390 ::boost::mpl::int_<1> >,
Chris@16 391 // we need row chaining
Chris@16 392 ::boost::mpl::push_back< ::boost::mpl::placeholders::_1,
Chris@16 393 make_chain_row_from_map_entry< ::boost::mpl::placeholders::_2> >,
Chris@16 394 // just one row, no chaining, we rebuild the row like it was before
Chris@16 395 ::boost::mpl::push_back< ::boost::mpl::placeholders::_1,
Chris@16 396 get_first_element_pair_second< ::boost::mpl::placeholders::_2> >
Chris@16 397 > >::type chained_rows;
Chris@16 398 // Go back and fill in cells for matching transitions.
Chris@16 399 ::boost::mpl::for_each<chained_rows>(init_cell(this));
Chris@16 400 }
Chris@16 401
Chris@16 402 // The singleton instance.
Chris@16 403 static const dispatch_table instance;
Chris@16 404
Chris@16 405 public: // data members
Chris@16 406 // +1 => 0 is reserved for this fsm (internal transitions)
Chris@16 407 cell entries[max_state+1];
Chris@16 408 };
Chris@16 409
Chris@16 410 }}} // boost::msm::back
Chris@16 411
Chris@16 412
Chris@16 413 #endif //BOOST_MSM_BACK_DISPATCH_TABLE_H
Chris@16 414