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

source code of boost/libs/msm/include/boost/msm/back11/dispatch_table.hpp