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

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