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_STATEMACHINE_H |
12 | #define BOOST_MSM_BACK_STATEMACHINE_H |
13 | |
14 | #include <exception> |
15 | #include <vector> |
16 | #include <functional> |
17 | #include <numeric> |
18 | #include <utility> |
19 | #include <algorithm> |
20 | |
21 | #include <boost/core/no_exceptions_support.hpp> |
22 | |
23 | #include <boost/core/ignore_unused.hpp> |
24 | #include <boost/mpl/contains.hpp> |
25 | #include <boost/mpl/deref.hpp> |
26 | #include <boost/mpl/assert.hpp> |
27 | |
28 | #include <boost/fusion/container/vector/convert.hpp> |
29 | #include <boost/fusion/include/as_vector.hpp> |
30 | #include <boost/fusion/include/as_set.hpp> |
31 | #include <boost/fusion/container/set.hpp> |
32 | #include <boost/fusion/include/set.hpp> |
33 | #include <boost/fusion/include/set_fwd.hpp> |
34 | #include <boost/fusion/include/mpl.hpp> |
35 | #include <boost/fusion/sequence/intrinsic/at_key.hpp> |
36 | #include <boost/fusion/include/at_key.hpp> |
37 | #include <boost/fusion/algorithm/iteration/for_each.hpp> |
38 | #include <boost/fusion/include/for_each.hpp> |
39 | |
40 | #include <boost/assert.hpp> |
41 | #include <boost/ref.hpp> |
42 | #include <boost/type_traits.hpp> |
43 | #include <boost/utility/enable_if.hpp> |
44 | #include <boost/type_traits/is_convertible.hpp> |
45 | |
46 | #include <boost/bind/bind.hpp> |
47 | #include <boost/function.hpp> |
48 | #ifndef BOOST_NO_RTTI |
49 | #include <boost/any.hpp> |
50 | #endif |
51 | |
52 | #include <boost/serialization/base_object.hpp> |
53 | |
54 | #include <boost/parameter.hpp> |
55 | |
56 | #include <boost/msm/active_state_switching_policies.hpp> |
57 | #include <boost/msm/row_tags.hpp> |
58 | #include <boost/msm/msm_grammar.hpp> |
59 | #include <boost/msm/back/fold_to_list.hpp> |
60 | #include <boost/msm/back/metafunctions.hpp> |
61 | #include <boost/msm/back/history_policies.hpp> |
62 | #include <boost/msm/back/common_types.hpp> |
63 | #include <boost/msm/back/args.hpp> |
64 | #include <boost/msm/back/default_compile_policy.hpp> |
65 | #include <boost/msm/back/dispatch_table.hpp> |
66 | #include <boost/msm/back/no_fsm_check.hpp> |
67 | #include <boost/msm/back/queue_container_deque.hpp> |
68 | |
69 | BOOST_MPL_HAS_XXX_TRAIT_DEF(accept_sig) |
70 | BOOST_MPL_HAS_XXX_TRAIT_DEF(no_automatic_create) |
71 | BOOST_MPL_HAS_XXX_TRAIT_DEF(non_forwarding_flag) |
72 | BOOST_MPL_HAS_XXX_TRAIT_DEF(direct_entry) |
73 | BOOST_MPL_HAS_XXX_TRAIT_DEF(initial_event) |
74 | BOOST_MPL_HAS_XXX_TRAIT_DEF(final_event) |
75 | BOOST_MPL_HAS_XXX_TRAIT_DEF(do_serialize) |
76 | BOOST_MPL_HAS_XXX_TRAIT_DEF(history_policy) |
77 | BOOST_MPL_HAS_XXX_TRAIT_DEF(fsm_check) |
78 | BOOST_MPL_HAS_XXX_TRAIT_DEF(compile_policy) |
79 | BOOST_MPL_HAS_XXX_TRAIT_DEF(queue_container_policy) |
80 | BOOST_MPL_HAS_XXX_TRAIT_DEF(using_declared_table) |
81 | BOOST_MPL_HAS_XXX_TRAIT_DEF(event_queue_before_deferred_queue) |
82 | |
83 | #ifndef BOOST_MSM_CONSTRUCTOR_ARG_SIZE |
84 | #define BOOST_MSM_CONSTRUCTOR_ARG_SIZE 5 // default max number of arguments for constructors |
85 | #endif |
86 | |
87 | namespace boost { namespace msm { namespace back |
88 | { |
89 | // event used internally for wrapping a direct entry |
90 | template <class StateType,class Event> |
91 | struct direct_entry_event |
92 | { |
93 | typedef int direct_entry; |
94 | typedef StateType active_state; |
95 | typedef Event contained_event; |
96 | |
97 | direct_entry_event(Event const& evt):m_event(evt){} |
98 | Event const& m_event; |
99 | }; |
100 | |
101 | BOOST_PARAMETER_TEMPLATE_KEYWORD(front_end) |
102 | BOOST_PARAMETER_TEMPLATE_KEYWORD(history_policy) |
103 | BOOST_PARAMETER_TEMPLATE_KEYWORD(compile_policy) |
104 | BOOST_PARAMETER_TEMPLATE_KEYWORD(fsm_check_policy) |
105 | BOOST_PARAMETER_TEMPLATE_KEYWORD(queue_container_policy) |
106 | |
107 | typedef ::boost::parameter::parameters< |
108 | ::boost::parameter::required< ::boost::msm::back::tag::front_end > |
109 | , ::boost::parameter::optional< |
110 | ::boost::parameter::deduced< ::boost::msm::back::tag::history_policy>, has_history_policy< ::boost::mpl::_ > |
111 | > |
112 | , ::boost::parameter::optional< |
113 | ::boost::parameter::deduced< ::boost::msm::back::tag::compile_policy>, has_compile_policy< ::boost::mpl::_ > |
114 | > |
115 | , ::boost::parameter::optional< |
116 | ::boost::parameter::deduced< ::boost::msm::back::tag::fsm_check_policy>, has_fsm_check< ::boost::mpl::_ > |
117 | > |
118 | , ::boost::parameter::optional< |
119 | ::boost::parameter::deduced< ::boost::msm::back::tag::queue_container_policy>, |
120 | has_queue_container_policy< ::boost::mpl::_ > |
121 | > |
122 | > state_machine_signature; |
123 | |
124 | // just here to disable use of proto when not needed |
125 | template <class T, class F,class Enable=void> |
126 | struct make_euml_terminal; |
127 | template <class T,class F> |
128 | struct make_euml_terminal<T,F,typename ::boost::disable_if<has_using_declared_table<F> >::type> |
129 | {}; |
130 | template <class T,class F> |
131 | struct make_euml_terminal<T,F,typename ::boost::enable_if<has_using_declared_table<F> >::type> |
132 | : public proto::extends<typename proto::terminal< boost::msm::state_tag>::type, T, boost::msm::state_domain> |
133 | {}; |
134 | |
135 | // library-containing class for state machines. Pass the actual FSM class as |
136 | // the Concrete parameter. |
137 | // A0=Derived,A1=NoHistory,A2=CompilePolicy,A3=FsmCheckPolicy > |
138 | template < |
139 | class A0 |
140 | , class A1 = parameter::void_ |
141 | , class A2 = parameter::void_ |
142 | , class A3 = parameter::void_ |
143 | , class A4 = parameter::void_ |
144 | > |
145 | class state_machine : //public Derived |
146 | public ::boost::parameter::binding< |
147 | typename state_machine_signature::bind<A0,A1,A2,A3,A4>::type, ::boost::msm::back::tag::front_end |
148 | >::type |
149 | , public make_euml_terminal<state_machine<A0,A1,A2,A3,A4>, |
150 | typename ::boost::parameter::binding< |
151 | typename state_machine_signature::bind<A0,A1,A2,A3,A4>::type, ::boost::msm::back::tag::front_end |
152 | >::type |
153 | > |
154 | { |
155 | public: |
156 | // Create ArgumentPack |
157 | typedef typename |
158 | state_machine_signature::bind<A0,A1,A2,A3,A4>::type |
159 | state_machine_args; |
160 | |
161 | // Extract first logical parameter. |
162 | typedef typename ::boost::parameter::binding< |
163 | state_machine_args, ::boost::msm::back::tag::front_end>::type Derived; |
164 | |
165 | typedef typename ::boost::parameter::binding< |
166 | state_machine_args, ::boost::msm::back::tag::history_policy, NoHistory >::type HistoryPolicy; |
167 | |
168 | typedef typename ::boost::parameter::binding< |
169 | state_machine_args, ::boost::msm::back::tag::compile_policy, favor_runtime_speed >::type CompilePolicy; |
170 | |
171 | typedef typename ::boost::parameter::binding< |
172 | state_machine_args, ::boost::msm::back::tag::fsm_check_policy, no_fsm_check >::type FsmCheckPolicy; |
173 | |
174 | typedef typename ::boost::parameter::binding< |
175 | state_machine_args, ::boost::msm::back::tag::queue_container_policy, |
176 | queue_container_deque >::type QueueContainerPolicy; |
177 | |
178 | private: |
179 | |
180 | typedef boost::msm::back::state_machine< |
181 | A0,A1,A2,A3,A4> library_sm; |
182 | |
183 | typedef ::boost::function< |
184 | execute_return ()> transition_fct; |
185 | typedef ::boost::function< |
186 | execute_return () > deferred_fct; |
187 | typedef typename QueueContainerPolicy:: |
188 | template In< |
189 | std::pair<deferred_fct,char> >::type deferred_events_queue_t; |
190 | typedef typename QueueContainerPolicy:: |
191 | template In<transition_fct>::type events_queue_t; |
192 | |
193 | typedef typename boost::mpl::eval_if< |
194 | typename is_active_state_switch_policy<Derived>::type, |
195 | get_active_state_switch_policy<Derived>, |
196 | // default |
197 | ::boost::mpl::identity<active_state_switch_after_entry> |
198 | >::type active_state_switching; |
199 | |
200 | typedef bool (*flag_handler)(library_sm const&); |
201 | |
202 | // all state machines are friend with each other to allow embedding any of them in another fsm |
203 | template <class ,class , class, class, class |
204 | > friend class boost::msm::back::state_machine; |
205 | |
206 | // helper to add, if needed, visitors to all states |
207 | // version without visitors |
208 | template <class StateType,class Enable=void> |
209 | struct visitor_fct_helper |
210 | { |
211 | public: |
212 | visitor_fct_helper(){} |
213 | void fill_visitors(int) |
214 | { |
215 | } |
216 | template <class FCT> |
217 | void insert(int,FCT) |
218 | { |
219 | } |
220 | template <class VISITOR> |
221 | void execute(int,VISITOR) |
222 | { |
223 | } |
224 | }; |
225 | // version with visitors |
226 | template <class StateType> |
227 | struct visitor_fct_helper<StateType,typename ::boost::enable_if<has_accept_sig<StateType> >::type> |
228 | { |
229 | public: |
230 | visitor_fct_helper():m_state_visitors(){} |
231 | void fill_visitors(int number_of_states) |
232 | { |
233 | m_state_visitors.resize(number_of_states); |
234 | } |
235 | template <class FCT> |
236 | void insert(int index,FCT fct) |
237 | { |
238 | m_state_visitors[index]=fct; |
239 | } |
240 | void execute(int index) |
241 | { |
242 | m_state_visitors[index](); |
243 | } |
244 | |
245 | #define MSM_VISITOR_HELPER_EXECUTE_SUB(z, n, unused) ARG ## n vis ## n |
246 | #define MSM_VISITOR_HELPER_EXECUTE(z, n, unused) \ |
247 | template <BOOST_PP_ENUM_PARAMS(n, class ARG)> \ |
248 | void execute(int index BOOST_PP_COMMA_IF(n) \ |
249 | BOOST_PP_ENUM(n, MSM_VISITOR_HELPER_EXECUTE_SUB, ~ ) ) \ |
250 | { \ |
251 | m_state_visitors[index](BOOST_PP_ENUM_PARAMS(n,vis)); \ |
252 | } |
253 | BOOST_PP_REPEAT_FROM_TO(1,BOOST_PP_ADD(BOOST_MSM_VISITOR_ARG_SIZE,1), MSM_VISITOR_HELPER_EXECUTE, ~) |
254 | #undef MSM_VISITOR_HELPER_EXECUTE |
255 | #undef MSM_VISITOR_HELPER_EXECUTE_SUB |
256 | private: |
257 | typedef typename StateType::accept_sig::type visitor_fct; |
258 | typedef std::vector<visitor_fct> visitors; |
259 | |
260 | visitors m_state_visitors; |
261 | }; |
262 | |
263 | template <class StateType,class Enable=int> |
264 | struct deferred_msg_queue_helper |
265 | { |
266 | void clear(){} |
267 | }; |
268 | template <class StateType> |
269 | struct deferred_msg_queue_helper<StateType, |
270 | typename ::boost::enable_if< |
271 | typename ::boost::msm::back::has_fsm_deferred_events<StateType>::type,int >::type> |
272 | { |
273 | public: |
274 | deferred_msg_queue_helper():m_deferred_events_queue(),m_cur_seq(0){} |
275 | void clear() |
276 | { |
277 | m_deferred_events_queue.clear(); |
278 | } |
279 | deferred_events_queue_t m_deferred_events_queue; |
280 | char m_cur_seq; |
281 | }; |
282 | |
283 | public: |
284 | // tags |
285 | typedef int composite_tag; |
286 | |
287 | // in case someone needs to know |
288 | typedef HistoryPolicy history_policy; |
289 | |
290 | struct InitEvent { }; |
291 | struct ExitEvent { }; |
292 | // flag handling |
293 | struct Flag_AND |
294 | { |
295 | typedef std::logical_and<bool> type; |
296 | }; |
297 | struct Flag_OR |
298 | { |
299 | typedef std::logical_or<bool> type; |
300 | }; |
301 | typedef typename Derived::BaseAllStates BaseState; |
302 | typedef Derived ConcreteSM; |
303 | |
304 | // if the front-end fsm provides an initial_event typedef, replace InitEvent by this one |
305 | typedef typename ::boost::mpl::eval_if< |
306 | typename has_initial_event<Derived>::type, |
307 | get_initial_event<Derived>, |
308 | ::boost::mpl::identity<InitEvent> |
309 | >::type fsm_initial_event; |
310 | |
311 | // if the front-end fsm provides an exit_event typedef, replace ExitEvent by this one |
312 | typedef typename ::boost::mpl::eval_if< |
313 | typename has_final_event<Derived>::type, |
314 | get_final_event<Derived>, |
315 | ::boost::mpl::identity<ExitEvent> |
316 | >::type fsm_final_event; |
317 | |
318 | template <class ExitPoint> |
319 | struct exit_pt : public ExitPoint |
320 | { |
321 | // tags |
322 | typedef ExitPoint wrapped_exit; |
323 | typedef int pseudo_exit; |
324 | typedef library_sm owner; |
325 | typedef int no_automatic_create; |
326 | typedef typename |
327 | ExitPoint::event Event; |
328 | typedef ::boost::function<execute_return (Event const&)> |
329 | forwarding_function; |
330 | |
331 | // forward event to the higher-level FSM |
332 | template <class ForwardEvent> |
333 | void forward_event(ForwardEvent const& incomingEvent) |
334 | { |
335 | // use helper to forward or not |
336 | ForwardHelper< ::boost::is_convertible<ForwardEvent,Event>::value>::helper(incomingEvent,m_forward); |
337 | } |
338 | void set_forward_fct(::boost::function<execute_return (Event const&)> fct) |
339 | { |
340 | m_forward = fct; |
341 | } |
342 | exit_pt():m_forward(){} |
343 | // by assignments, we keep our forwarding functor unchanged as our containing SM did not change |
344 | template <class RHS> |
345 | exit_pt(RHS&):m_forward(){} |
346 | exit_pt<ExitPoint>& operator= (const exit_pt<ExitPoint>& ) |
347 | { |
348 | return *this; |
349 | } |
350 | private: |
351 | forwarding_function m_forward; |
352 | |
353 | // using partial specialization instead of enable_if because of VC8 bug |
354 | template <bool OwnEvent, int Dummy=0> |
355 | struct ForwardHelper |
356 | { |
357 | template <class ForwardEvent> |
358 | static void helper(ForwardEvent const& ,forwarding_function& ) |
359 | { |
360 | // Not our event, assert |
361 | BOOST_ASSERT(false); |
362 | } |
363 | }; |
364 | template <int Dummy> |
365 | struct ForwardHelper<true,Dummy> |
366 | { |
367 | template <class ForwardEvent> |
368 | static void helper(ForwardEvent const& incomingEvent,forwarding_function& forward_fct) |
369 | { |
370 | // call if handler set, if not, this state is simply a terminate state |
371 | if (forward_fct) |
372 | forward_fct(incomingEvent); |
373 | } |
374 | }; |
375 | |
376 | }; |
377 | template <class EntryPoint> |
378 | struct entry_pt : public EntryPoint |
379 | { |
380 | // tags |
381 | typedef EntryPoint wrapped_entry; |
382 | typedef int pseudo_entry; |
383 | typedef library_sm owner; |
384 | typedef int no_automatic_create; |
385 | }; |
386 | template <class EntryPoint> |
387 | struct direct : public EntryPoint |
388 | { |
389 | // tags |
390 | typedef EntryPoint wrapped_entry; |
391 | typedef int explicit_entry_state; |
392 | typedef library_sm owner; |
393 | typedef int no_automatic_create; |
394 | }; |
395 | typedef typename get_number_of_regions<typename Derived::initial_state>::type nr_regions; |
396 | // Template used to form rows in the transition table |
397 | template< |
398 | typename ROW |
399 | > |
400 | struct row_ |
401 | { |
402 | //typedef typename ROW::Source T1; |
403 | typedef typename make_entry<typename ROW::Source,library_sm>::type T1; |
404 | typedef typename make_exit<typename ROW::Target,library_sm>::type T2; |
405 | typedef typename ROW::Evt transition_event; |
406 | // if the source is an exit pseudo state, then |
407 | // current_state_type becomes the result of get_owner |
408 | // meaning the containing SM from which the exit occurs |
409 | typedef typename ::boost::mpl::eval_if< |
410 | typename has_pseudo_exit<T1>::type, |
411 | get_owner<T1,library_sm>, |
412 | ::boost::mpl::identity<typename ROW::Source> >::type current_state_type; |
413 | |
414 | // if Target is a sequence, then we have a fork and expect a sequence of explicit_entry |
415 | // else if Target is an explicit_entry, next_state_type becomes the result of get_owner |
416 | // meaning the containing SM if the row is "outside" the containing SM or else the explicit_entry state itself |
417 | typedef typename ::boost::mpl::eval_if< |
418 | typename ::boost::mpl::is_sequence<T2>::type, |
419 | get_fork_owner<T2,library_sm>, |
420 | ::boost::mpl::eval_if< |
421 | typename has_no_automatic_create<T2>::type, |
422 | get_owner<T2,library_sm>, |
423 | ::boost::mpl::identity<T2> > |
424 | >::type next_state_type; |
425 | |
426 | // if a guard condition is here, call it to check that the event is accepted |
427 | static bool check_guard(library_sm& fsm,transition_event const& evt) |
428 | { |
429 | if ( ROW::guard_call(fsm,evt, |
430 | ::boost::fusion::at_key<current_state_type>(fsm.m_substate_list), |
431 | ::boost::fusion::at_key<next_state_type>(fsm.m_substate_list), |
432 | fsm.m_substate_list ) ) |
433 | return true; |
434 | return false; |
435 | } |
436 | // Take the transition action and return the next state. |
437 | static HandledEnum execute(library_sm& fsm, int region_index, int state, transition_event& evt) |
438 | { |
439 | |
440 | BOOST_STATIC_CONSTANT(int, current_state = (get_state_id<stt,current_state_type>::type::value)); |
441 | BOOST_STATIC_CONSTANT(int, next_state = (get_state_id<stt,next_state_type>::type::value)); |
442 | boost::ignore_unused(state); // Avoid warnings if BOOST_ASSERT expands to nothing. |
443 | BOOST_ASSERT(state == (current_state)); |
444 | // if T1 is an exit pseudo state, then take the transition only if the pseudo exit state is active |
445 | if (has_pseudo_exit<T1>::type::value && |
446 | !is_exit_state_active<T1,get_owner<T1,library_sm> >(fsm)) |
447 | { |
448 | return HANDLED_FALSE; |
449 | } |
450 | if (!check_guard(fsm,evt)) |
451 | { |
452 | // guard rejected the event, we stay in the current one |
453 | return HANDLED_GUARD_REJECT; |
454 | } |
455 | fsm.m_states[region_index] = active_state_switching::after_guard(current_state,next_state); |
456 | |
457 | // the guard condition has already been checked |
458 | execute_exit<current_state_type> |
459 | (::boost::fusion::at_key<current_state_type>(fsm.m_substate_list),evt,fsm); |
460 | fsm.m_states[region_index] = active_state_switching::after_exit(current_state,next_state); |
461 | |
462 | // then call the action method |
463 | HandledEnum res = ROW::action_call(fsm,evt, |
464 | ::boost::fusion::at_key<current_state_type>(fsm.m_substate_list), |
465 | ::boost::fusion::at_key<next_state_type>(fsm.m_substate_list), |
466 | fsm.m_substate_list); |
467 | fsm.m_states[region_index] = active_state_switching::after_action(current_state,next_state); |
468 | |
469 | // and finally the entry method of the new current state |
470 | convert_event_and_execute_entry<next_state_type,T2> |
471 | (::boost::fusion::at_key<next_state_type>(fsm.m_substate_list),evt,fsm); |
472 | fsm.m_states[region_index] = active_state_switching::after_entry(current_state,next_state); |
473 | return res; |
474 | } |
475 | }; |
476 | |
477 | // row having only a guard condition |
478 | template< |
479 | typename ROW |
480 | > |
481 | struct g_row_ |
482 | { |
483 | //typedef typename ROW::Source T1; |
484 | typedef typename make_entry<typename ROW::Source,library_sm>::type T1; |
485 | typedef typename make_exit<typename ROW::Target,library_sm>::type T2; |
486 | typedef typename ROW::Evt transition_event; |
487 | // if the source is an exit pseudo state, then |
488 | // current_state_type becomes the result of get_owner |
489 | // meaning the containing SM from which the exit occurs |
490 | typedef typename ::boost::mpl::eval_if< |
491 | typename has_pseudo_exit<T1>::type, |
492 | get_owner<T1,library_sm>, |
493 | ::boost::mpl::identity<typename ROW::Source> >::type current_state_type; |
494 | |
495 | // if Target is a sequence, then we have a fork and expect a sequence of explicit_entry |
496 | // else if Target is an explicit_entry, next_state_type becomes the result of get_owner |
497 | // meaning the containing SM if the row is "outside" the containing SM or else the explicit_entry state itself |
498 | typedef typename ::boost::mpl::eval_if< |
499 | typename ::boost::mpl::is_sequence<T2>::type, |
500 | get_fork_owner<T2,library_sm>, |
501 | ::boost::mpl::eval_if< |
502 | typename has_no_automatic_create<T2>::type, |
503 | get_owner<T2,library_sm>, |
504 | ::boost::mpl::identity<T2> > |
505 | >::type next_state_type; |
506 | |
507 | // if a guard condition is defined, call it to check that the event is accepted |
508 | static bool check_guard(library_sm& fsm,transition_event const& evt) |
509 | { |
510 | if ( ROW::guard_call(fsm,evt, |
511 | ::boost::fusion::at_key<current_state_type>(fsm.m_substate_list), |
512 | ::boost::fusion::at_key<next_state_type>(fsm.m_substate_list), |
513 | fsm.m_substate_list )) |
514 | return true; |
515 | return false; |
516 | } |
517 | // Take the transition action and return the next state. |
518 | static HandledEnum execute(library_sm& fsm, int region_index, int state, transition_event const& evt) |
519 | { |
520 | BOOST_STATIC_CONSTANT(int, current_state = (get_state_id<stt,current_state_type>::type::value)); |
521 | BOOST_STATIC_CONSTANT(int, next_state = (get_state_id<stt,next_state_type>::type::value)); |
522 | boost::ignore_unused(state); // Avoid warnings if BOOST_ASSERT expands to nothing. |
523 | BOOST_ASSERT(state == (current_state)); |
524 | // if T1 is an exit pseudo state, then take the transition only if the pseudo exit state is active |
525 | if (has_pseudo_exit<T1>::type::value && |
526 | !is_exit_state_active<T1,get_owner<T1,library_sm> >(fsm)) |
527 | { |
528 | return HANDLED_FALSE; |
529 | } |
530 | if (!check_guard(fsm,evt)) |
531 | { |
532 | // guard rejected the event, we stay in the current one |
533 | return HANDLED_GUARD_REJECT; |
534 | } |
535 | fsm.m_states[region_index] = active_state_switching::after_guard(current_state,next_state); |
536 | |
537 | // the guard condition has already been checked |
538 | execute_exit<current_state_type> |
539 | (::boost::fusion::at_key<current_state_type>(fsm.m_substate_list),evt,fsm); |
540 | fsm.m_states[region_index] = active_state_switching::after_exit(current_state,next_state); |
541 | fsm.m_states[region_index] = active_state_switching::after_action(current_state,next_state); |
542 | |
543 | // and finally the entry method of the new current state |
544 | convert_event_and_execute_entry<next_state_type,T2> |
545 | (::boost::fusion::at_key<next_state_type>(fsm.m_substate_list),evt,fsm); |
546 | fsm.m_states[region_index] = active_state_switching::after_entry(current_state,next_state); |
547 | return HANDLED_TRUE; |
548 | } |
549 | }; |
550 | |
551 | // row having only an action method |
552 | template< |
553 | typename ROW |
554 | > |
555 | struct a_row_ |
556 | { |
557 | //typedef typename ROW::Source T1; |
558 | typedef typename make_entry<typename ROW::Source,library_sm>::type T1; |
559 | typedef typename make_exit<typename ROW::Target,library_sm>::type T2; |
560 | typedef typename ROW::Evt transition_event; |
561 | // if the source is an exit pseudo state, then |
562 | // current_state_type becomes the result of get_owner |
563 | // meaning the containing SM from which the exit occurs |
564 | typedef typename ::boost::mpl::eval_if< |
565 | typename has_pseudo_exit<T1>::type, |
566 | get_owner<T1,library_sm>, |
567 | ::boost::mpl::identity<typename ROW::Source> >::type current_state_type; |
568 | |
569 | // if Target is a sequence, then we have a fork and expect a sequence of explicit_entry |
570 | // else if Target is an explicit_entry, next_state_type becomes the result of get_owner |
571 | // meaning the containing SM if the row is "outside" the containing SM or else the explicit_entry state itself |
572 | typedef typename ::boost::mpl::eval_if< |
573 | typename ::boost::mpl::is_sequence<T2>::type, |
574 | get_fork_owner<T2,library_sm>, |
575 | ::boost::mpl::eval_if< |
576 | typename has_no_automatic_create<T2>::type, |
577 | get_owner<T2,library_sm>, |
578 | ::boost::mpl::identity<T2> > |
579 | >::type next_state_type; |
580 | |
581 | // Take the transition action and return the next state. |
582 | static HandledEnum execute(library_sm& fsm, int region_index, int state, transition_event& evt) |
583 | { |
584 | BOOST_STATIC_CONSTANT(int, current_state = (get_state_id<stt,current_state_type>::type::value)); |
585 | BOOST_STATIC_CONSTANT(int, next_state = (get_state_id<stt,next_state_type>::type::value)); |
586 | boost::ignore_unused(state); // Avoid warnings if BOOST_ASSERT expands to nothing. |
587 | BOOST_ASSERT(state == (current_state)); |
588 | |
589 | // if T1 is an exit pseudo state, then take the transition only if the pseudo exit state is active |
590 | if (has_pseudo_exit<T1>::type::value && |
591 | !is_exit_state_active<T1,get_owner<T1,library_sm> >(fsm)) |
592 | { |
593 | return HANDLED_FALSE; |
594 | } |
595 | fsm.m_states[region_index] = active_state_switching::after_guard(current_state,next_state); |
596 | |
597 | // no need to check the guard condition |
598 | // first call the exit method of the current state |
599 | execute_exit<current_state_type> |
600 | (::boost::fusion::at_key<current_state_type>(fsm.m_substate_list),evt,fsm); |
601 | fsm.m_states[region_index] = active_state_switching::after_exit(current_state,next_state); |
602 | |
603 | // then call the action method |
604 | HandledEnum res = ROW::action_call(fsm,evt, |
605 | ::boost::fusion::at_key<current_state_type>(fsm.m_substate_list), |
606 | ::boost::fusion::at_key<next_state_type>(fsm.m_substate_list), |
607 | fsm.m_substate_list); |
608 | fsm.m_states[region_index] = active_state_switching::after_action(current_state,next_state); |
609 | |
610 | // and finally the entry method of the new current state |
611 | convert_event_and_execute_entry<next_state_type,T2> |
612 | (::boost::fusion::at_key<next_state_type>(fsm.m_substate_list),evt,fsm); |
613 | fsm.m_states[region_index] = active_state_switching::after_entry(current_state,next_state); |
614 | return res; |
615 | } |
616 | }; |
617 | |
618 | // row having no guard condition or action, simply transitions |
619 | template< |
620 | typename ROW |
621 | > |
622 | struct _row_ |
623 | { |
624 | //typedef typename ROW::Source T1; |
625 | typedef typename make_entry<typename ROW::Source,library_sm>::type T1; |
626 | typedef typename make_exit<typename ROW::Target,library_sm>::type T2; |
627 | typedef typename ROW::Evt transition_event; |
628 | // if the source is an exit pseudo state, then |
629 | // current_state_type becomes the result of get_owner |
630 | // meaning the containing SM from which the exit occurs |
631 | typedef typename ::boost::mpl::eval_if< |
632 | typename has_pseudo_exit<T1>::type, |
633 | get_owner<T1,library_sm>, |
634 | ::boost::mpl::identity<typename ROW::Source> >::type current_state_type; |
635 | |
636 | // if Target is a sequence, then we have a fork and expect a sequence of explicit_entry |
637 | // else if Target is an explicit_entry, next_state_type becomes the result of get_owner |
638 | // meaning the containing SM if the row is "outside" the containing SM or else the explicit_entry state itself |
639 | typedef typename ::boost::mpl::eval_if< |
640 | typename ::boost::mpl::is_sequence<T2>::type, |
641 | get_fork_owner<T2,library_sm>, |
642 | ::boost::mpl::eval_if< |
643 | typename has_no_automatic_create<T2>::type, |
644 | get_owner<T2,library_sm>, |
645 | ::boost::mpl::identity<T2> > |
646 | >::type next_state_type; |
647 | |
648 | // Take the transition action and return the next state. |
649 | static HandledEnum execute(library_sm& fsm, int region_index, int state, transition_event const& evt) |
650 | { |
651 | BOOST_STATIC_CONSTANT(int, current_state = (get_state_id<stt,current_state_type>::type::value)); |
652 | BOOST_STATIC_CONSTANT(int, next_state = (get_state_id<stt,next_state_type>::type::value)); |
653 | boost::ignore_unused(state); // Avoid warnings if BOOST_ASSERT expands to nothing. |
654 | BOOST_ASSERT(state == (current_state)); |
655 | |
656 | // if T1 is an exit pseudo state, then take the transition only if the pseudo exit state is active |
657 | if (has_pseudo_exit<T1>::type::value && |
658 | !is_exit_state_active<T1,get_owner<T1,library_sm> >(fsm)) |
659 | { |
660 | return HANDLED_FALSE; |
661 | } |
662 | fsm.m_states[region_index] = active_state_switching::after_guard(current_state,next_state); |
663 | |
664 | // first call the exit method of the current state |
665 | execute_exit<current_state_type> |
666 | (::boost::fusion::at_key<current_state_type>(fsm.m_substate_list),evt,fsm); |
667 | fsm.m_states[region_index] = active_state_switching::after_exit(current_state,next_state); |
668 | fsm.m_states[region_index] = active_state_switching::after_action(current_state,next_state); |
669 | |
670 | |
671 | // and finally the entry method of the new current state |
672 | convert_event_and_execute_entry<next_state_type,T2> |
673 | (::boost::fusion::at_key<next_state_type>(fsm.m_substate_list),evt,fsm); |
674 | fsm.m_states[region_index] = active_state_switching::after_entry(current_state,next_state); |
675 | return HANDLED_TRUE; |
676 | } |
677 | }; |
678 | // "i" rows are rows for internal transitions |
679 | template< |
680 | typename ROW |
681 | > |
682 | struct irow_ |
683 | { |
684 | typedef typename make_entry<typename ROW::Source,library_sm>::type T1; |
685 | typedef typename make_exit<typename ROW::Target,library_sm>::type T2; |
686 | typedef typename ROW::Evt transition_event; |
687 | typedef typename ROW::Source current_state_type; |
688 | typedef T2 next_state_type; |
689 | |
690 | // if a guard condition is here, call it to check that the event is accepted |
691 | static bool check_guard(library_sm& fsm,transition_event const& evt) |
692 | { |
693 | if ( ROW::guard_call(fsm,evt, |
694 | ::boost::fusion::at_key<current_state_type>(fsm.m_substate_list), |
695 | ::boost::fusion::at_key<next_state_type>(fsm.m_substate_list), |
696 | fsm.m_substate_list)) |
697 | return true; |
698 | return false; |
699 | } |
700 | // Take the transition action and return the next state. |
701 | static HandledEnum execute(library_sm& fsm, int , int state, transition_event const& evt) |
702 | { |
703 | |
704 | BOOST_STATIC_CONSTANT(int, current_state = (get_state_id<stt,current_state_type>::type::value)); |
705 | boost::ignore_unused(state, current_state); // Avoid warnings if BOOST_ASSERT expands to nothing. |
706 | BOOST_ASSERT(state == (current_state)); |
707 | if (!check_guard(fsm,evt)) |
708 | { |
709 | // guard rejected the event, we stay in the current one |
710 | return HANDLED_GUARD_REJECT; |
711 | } |
712 | |
713 | // call the action method |
714 | HandledEnum res = ROW::action_call(fsm,evt, |
715 | ::boost::fusion::at_key<current_state_type>(fsm.m_substate_list), |
716 | ::boost::fusion::at_key<next_state_type>(fsm.m_substate_list), |
717 | fsm.m_substate_list); |
718 | return res; |
719 | } |
720 | }; |
721 | |
722 | // row having only a guard condition |
723 | template< |
724 | typename ROW |
725 | > |
726 | struct g_irow_ |
727 | { |
728 | typedef typename make_entry<typename ROW::Source,library_sm>::type T1; |
729 | typedef typename make_exit<typename ROW::Target,library_sm>::type T2; |
730 | typedef typename ROW::Evt transition_event; |
731 | typedef typename ROW::Source current_state_type; |
732 | typedef T2 next_state_type; |
733 | |
734 | // if a guard condition is defined, call it to check that the event is accepted |
735 | static bool check_guard(library_sm& fsm,transition_event const& evt) |
736 | { |
737 | if ( ROW::guard_call(fsm,evt, |
738 | ::boost::fusion::at_key<current_state_type>(fsm.m_substate_list), |
739 | ::boost::fusion::at_key<next_state_type>(fsm.m_substate_list), |
740 | fsm.m_substate_list) ) |
741 | return true; |
742 | return false; |
743 | } |
744 | // Take the transition action and return the next state. |
745 | static HandledEnum execute(library_sm& fsm, int , int state, transition_event const& evt) |
746 | { |
747 | BOOST_STATIC_CONSTANT(int, current_state = (get_state_id<stt,current_state_type>::type::value)); |
748 | boost::ignore_unused(state, current_state); // Avoid warnings if BOOST_ASSERT expands to nothing. |
749 | BOOST_ASSERT(state == (current_state)); |
750 | if (!check_guard(fsm,evt)) |
751 | { |
752 | // guard rejected the event, we stay in the current one |
753 | return HANDLED_GUARD_REJECT; |
754 | } |
755 | return HANDLED_TRUE; |
756 | } |
757 | }; |
758 | |
759 | // row having only an action method |
760 | template< |
761 | typename ROW |
762 | > |
763 | struct a_irow_ |
764 | { |
765 | typedef typename make_entry<typename ROW::Source,library_sm>::type T1; |
766 | typedef typename make_exit<typename ROW::Target,library_sm>::type T2; |
767 | |
768 | typedef typename ROW::Evt transition_event; |
769 | typedef typename ROW::Source current_state_type; |
770 | typedef T2 next_state_type; |
771 | |
772 | // Take the transition action and return the next state. |
773 | static HandledEnum execute(library_sm& fsm, int , int state, transition_event const& evt) |
774 | { |
775 | BOOST_STATIC_CONSTANT(int, current_state = (get_state_id<stt,current_state_type>::type::value)); |
776 | boost::ignore_unused(state, current_state); // Avoid warnings if BOOST_ASSERT expands to nothing. |
777 | BOOST_ASSERT(state == (current_state)); |
778 | |
779 | // call the action method |
780 | HandledEnum res = ROW::action_call(fsm,evt, |
781 | ::boost::fusion::at_key<current_state_type>(fsm.m_substate_list), |
782 | ::boost::fusion::at_key<next_state_type>(fsm.m_substate_list), |
783 | fsm.m_substate_list); |
784 | |
785 | return res; |
786 | } |
787 | }; |
788 | // row simply ignoring the event |
789 | template< |
790 | typename ROW |
791 | > |
792 | struct _irow_ |
793 | { |
794 | typedef typename make_entry<typename ROW::Source,library_sm>::type T1; |
795 | typedef typename make_exit<typename ROW::Target,library_sm>::type T2; |
796 | typedef typename ROW::Evt transition_event; |
797 | typedef typename ROW::Source current_state_type; |
798 | typedef T2 next_state_type; |
799 | |
800 | // Take the transition action and return the next state. |
801 | static HandledEnum execute(library_sm& , int , int state, transition_event const& ) |
802 | { |
803 | BOOST_STATIC_CONSTANT(int, current_state = (get_state_id<stt,current_state_type>::type::value)); |
804 | boost::ignore_unused(state, current_state); // Avoid warnings if BOOST_ASSERT expands to nothing. |
805 | BOOST_ASSERT(state == (current_state)); |
806 | return HANDLED_TRUE; |
807 | } |
808 | }; |
809 | // transitions internal to this state machine (no substate involved) |
810 | template< |
811 | typename ROW, |
812 | typename StateType |
813 | > |
814 | struct internal_ |
815 | { |
816 | typedef StateType current_state_type; |
817 | typedef StateType next_state_type; |
818 | typedef typename ROW::Evt transition_event; |
819 | |
820 | // if a guard condition is here, call it to check that the event is accepted |
821 | static bool check_guard(library_sm& fsm,transition_event const& evt) |
822 | { |
823 | if ( ROW::guard_call(fsm,evt, |
824 | ::boost::fusion::at_key<StateType>(fsm.m_substate_list), |
825 | ::boost::fusion::at_key<StateType>(fsm.m_substate_list), |
826 | fsm.m_substate_list) ) |
827 | return true; |
828 | return false; |
829 | } |
830 | // Take the transition action and return the next state. |
831 | static HandledEnum execute(library_sm& fsm, int , int , transition_event const& evt) |
832 | { |
833 | if (!check_guard(fsm,evt)) |
834 | { |
835 | // guard rejected the event, we stay in the current one |
836 | return HANDLED_GUARD_REJECT; |
837 | } |
838 | |
839 | // then call the action method |
840 | HandledEnum res = ROW::action_call(fsm,evt, |
841 | ::boost::fusion::at_key<StateType>(fsm.m_substate_list), |
842 | ::boost::fusion::at_key<StateType>(fsm.m_substate_list), |
843 | fsm.m_substate_list); |
844 | return res; |
845 | } |
846 | }; |
847 | template< |
848 | typename ROW |
849 | > |
850 | struct internal_ <ROW,library_sm> |
851 | { |
852 | typedef library_sm current_state_type; |
853 | typedef library_sm next_state_type; |
854 | typedef typename ROW::Evt transition_event; |
855 | |
856 | // if a guard condition is here, call it to check that the event is accepted |
857 | static bool check_guard(library_sm& fsm,transition_event const& evt) |
858 | { |
859 | if ( ROW::guard_call(fsm,evt, |
860 | fsm, |
861 | fsm, |
862 | fsm.m_substate_list) ) |
863 | return true; |
864 | return false; |
865 | } |
866 | // Take the transition action and return the next state. |
867 | static HandledEnum execute(library_sm& fsm, int , int , transition_event const& evt) |
868 | { |
869 | if (!check_guard(fsm,evt)) |
870 | { |
871 | // guard rejected the event, we stay in the current one |
872 | return HANDLED_GUARD_REJECT; |
873 | } |
874 | |
875 | // then call the action method |
876 | HandledEnum res = ROW::action_call(fsm,evt, |
877 | fsm, |
878 | fsm, |
879 | fsm.m_substate_list); |
880 | return res; |
881 | } |
882 | }; |
883 | |
884 | template< |
885 | typename ROW, |
886 | typename StateType |
887 | > |
888 | struct a_internal_ |
889 | { |
890 | typedef StateType current_state_type; |
891 | typedef StateType next_state_type; |
892 | typedef typename ROW::Evt transition_event; |
893 | |
894 | // Take the transition action and return the next state. |
895 | static HandledEnum execute(library_sm& fsm, int, int, transition_event const& evt) |
896 | { |
897 | // then call the action method |
898 | HandledEnum res = ROW::action_call(fsm,evt, |
899 | ::boost::fusion::at_key<StateType>(fsm.m_substate_list), |
900 | ::boost::fusion::at_key<StateType>(fsm.m_substate_list), |
901 | fsm.m_substate_list); |
902 | return res; |
903 | } |
904 | }; |
905 | template< |
906 | typename ROW |
907 | > |
908 | struct a_internal_ <ROW,library_sm> |
909 | { |
910 | typedef library_sm current_state_type; |
911 | typedef library_sm next_state_type; |
912 | typedef typename ROW::Evt transition_event; |
913 | |
914 | // Take the transition action and return the next state. |
915 | static HandledEnum execute(library_sm& fsm, int, int, transition_event const& evt) |
916 | { |
917 | // then call the action method |
918 | HandledEnum res = ROW::action_call(fsm,evt, |
919 | fsm, |
920 | fsm, |
921 | fsm.m_substate_list); |
922 | return res; |
923 | } |
924 | }; |
925 | template< |
926 | typename ROW, |
927 | typename StateType |
928 | > |
929 | struct g_internal_ |
930 | { |
931 | typedef StateType current_state_type; |
932 | typedef StateType next_state_type; |
933 | typedef typename ROW::Evt transition_event; |
934 | |
935 | // if a guard condition is here, call it to check that the event is accepted |
936 | static bool check_guard(library_sm& fsm,transition_event const& evt) |
937 | { |
938 | if ( ROW::guard_call(fsm,evt, |
939 | ::boost::fusion::at_key<StateType>(fsm.m_substate_list), |
940 | ::boost::fusion::at_key<StateType>(fsm.m_substate_list), |
941 | fsm.m_substate_list) ) |
942 | return true; |
943 | return false; |
944 | } |
945 | // Take the transition action and return the next state. |
946 | static HandledEnum execute(library_sm& fsm, int, int, transition_event const& evt) |
947 | { |
948 | if (!check_guard(fsm,evt)) |
949 | { |
950 | // guard rejected the event, we stay in the current one |
951 | return HANDLED_GUARD_REJECT; |
952 | } |
953 | return HANDLED_TRUE; |
954 | } |
955 | }; |
956 | template< |
957 | typename ROW |
958 | > |
959 | struct g_internal_ <ROW,library_sm> |
960 | { |
961 | typedef library_sm current_state_type; |
962 | typedef library_sm next_state_type; |
963 | typedef typename ROW::Evt transition_event; |
964 | |
965 | // if a guard condition is here, call it to check that the event is accepted |
966 | static bool check_guard(library_sm& fsm,transition_event const& evt) |
967 | { |
968 | if ( ROW::guard_call(fsm,evt, |
969 | fsm, |
970 | fsm, |
971 | fsm.m_substate_list) ) |
972 | return true; |
973 | return false; |
974 | } |
975 | // Take the transition action and return the next state. |
976 | static HandledEnum execute(library_sm& fsm, int, int, transition_event const& evt) |
977 | { |
978 | if (!check_guard(fsm,evt)) |
979 | { |
980 | // guard rejected the event, we stay in the current one |
981 | return HANDLED_GUARD_REJECT; |
982 | } |
983 | return HANDLED_TRUE; |
984 | } |
985 | }; |
986 | template< |
987 | typename ROW, |
988 | typename StateType |
989 | > |
990 | struct _internal_ |
991 | { |
992 | typedef StateType current_state_type; |
993 | typedef StateType next_state_type; |
994 | typedef typename ROW::Evt transition_event; |
995 | static HandledEnum execute(library_sm& , int , int , transition_event const& ) |
996 | { |
997 | return HANDLED_TRUE; |
998 | } |
999 | }; |
1000 | template< |
1001 | typename ROW |
1002 | > |
1003 | struct _internal_ <ROW,library_sm> |
1004 | { |
1005 | typedef library_sm current_state_type; |
1006 | typedef library_sm next_state_type; |
1007 | typedef typename ROW::Evt transition_event; |
1008 | static HandledEnum execute(library_sm& , int , int , transition_event const& ) |
1009 | { |
1010 | return HANDLED_TRUE; |
1011 | } |
1012 | }; |
1013 | // Template used to form forwarding rows in the transition table for every row of a composite SM |
1014 | template< |
1015 | typename T1 |
1016 | , class Evt |
1017 | > |
1018 | struct frow |
1019 | { |
1020 | typedef T1 current_state_type; |
1021 | typedef T1 next_state_type; |
1022 | typedef Evt transition_event; |
1023 | // tag to find out if a row is a forwarding row |
1024 | typedef int is_frow; |
1025 | |
1026 | // Take the transition action and return the next state. |
1027 | static HandledEnum execute(library_sm& fsm, int region_index, int , transition_event const& evt) |
1028 | { |
1029 | // false as second parameter because this event is forwarded from outer fsm |
1030 | execute_return res = |
1031 | (::boost::fusion::at_key<current_state_type>(fsm.m_substate_list)).process_event_internal(evt); |
1032 | fsm.m_states[region_index]=get_state_id<stt,T1>::type::value; |
1033 | return res; |
1034 | } |
1035 | // helper metafunctions used by dispatch table and give the frow a new event |
1036 | // (used to avoid double entries in a table because of base events) |
1037 | template <class NewEvent> |
1038 | struct replace_event |
1039 | { |
1040 | typedef frow<T1,NewEvent> type; |
1041 | }; |
1042 | }; |
1043 | |
1044 | template <class Tag, class Transition,class StateType> |
1045 | struct create_backend_stt |
1046 | { |
1047 | }; |
1048 | template <class Transition,class StateType> |
1049 | struct create_backend_stt<g_row_tag,Transition,StateType> |
1050 | { |
1051 | typedef g_row_<Transition> type; |
1052 | }; |
1053 | template <class Transition,class StateType> |
1054 | struct create_backend_stt<a_row_tag,Transition,StateType> |
1055 | { |
1056 | typedef a_row_<Transition> type; |
1057 | }; |
1058 | template <class Transition,class StateType> |
1059 | struct create_backend_stt<_row_tag,Transition,StateType> |
1060 | { |
1061 | typedef _row_<Transition> type; |
1062 | }; |
1063 | template <class Transition,class StateType> |
1064 | struct create_backend_stt<row_tag,Transition,StateType> |
1065 | { |
1066 | typedef row_<Transition> type; |
1067 | }; |
1068 | // internal transitions |
1069 | template <class Transition,class StateType> |
1070 | struct create_backend_stt<g_irow_tag,Transition,StateType> |
1071 | { |
1072 | typedef g_irow_<Transition> type; |
1073 | }; |
1074 | template <class Transition,class StateType> |
1075 | struct create_backend_stt<a_irow_tag,Transition,StateType> |
1076 | { |
1077 | typedef a_irow_<Transition> type; |
1078 | }; |
1079 | template <class Transition,class StateType> |
1080 | struct create_backend_stt<irow_tag,Transition,StateType> |
1081 | { |
1082 | typedef irow_<Transition> type; |
1083 | }; |
1084 | template <class Transition,class StateType> |
1085 | struct create_backend_stt<_irow_tag,Transition,StateType> |
1086 | { |
1087 | typedef _irow_<Transition> type; |
1088 | }; |
1089 | template <class Transition,class StateType> |
1090 | struct create_backend_stt<sm_a_i_row_tag,Transition,StateType> |
1091 | { |
1092 | typedef a_internal_<Transition,StateType> type; |
1093 | }; |
1094 | template <class Transition,class StateType> |
1095 | struct create_backend_stt<sm_g_i_row_tag,Transition,StateType> |
1096 | { |
1097 | typedef g_internal_<Transition,StateType> type; |
1098 | }; |
1099 | template <class Transition,class StateType> |
1100 | struct create_backend_stt<sm_i_row_tag,Transition,StateType> |
1101 | { |
1102 | typedef internal_<Transition,StateType> type; |
1103 | }; |
1104 | template <class Transition,class StateType> |
1105 | struct create_backend_stt<sm__i_row_tag,Transition,StateType> |
1106 | { |
1107 | typedef _internal_<Transition,StateType> type; |
1108 | }; |
1109 | template <class Transition,class StateType=void> |
1110 | struct make_row_tag |
1111 | { |
1112 | typedef typename create_backend_stt<typename Transition::row_type_tag,Transition,StateType>::type type; |
1113 | }; |
1114 | |
1115 | // add to the stt the initial states which could be missing (if not being involved in a transition) |
1116 | template <class BaseType, class stt_simulated = typename BaseType::transition_table> |
1117 | struct create_real_stt |
1118 | { |
1119 | //typedef typename BaseType::transition_table stt_simulated; |
1120 | typedef typename ::boost::mpl::fold< |
1121 | stt_simulated,mpl::vector0<>, |
1122 | ::boost::mpl::push_back< ::boost::mpl::placeholders::_1, |
1123 | make_row_tag< ::boost::mpl::placeholders::_2 , BaseType > > |
1124 | >::type type; |
1125 | }; |
1126 | |
1127 | template <class Table,class Intermediate,class StateType> |
1128 | struct add_forwarding_row_helper |
1129 | { |
1130 | typedef typename generate_event_set<Table>::type all_events; |
1131 | typedef typename ::boost::mpl::fold< |
1132 | all_events, Intermediate, |
1133 | ::boost::mpl::push_back< ::boost::mpl::placeholders::_1, |
1134 | frow<StateType, ::boost::mpl::placeholders::_2> > >::type type; |
1135 | }; |
1136 | // gets the transition table from a composite and make from it a forwarding row |
1137 | template <class StateType,class IsComposite> |
1138 | struct get_internal_transition_table |
1139 | { |
1140 | // first get the table of a composite |
1141 | typedef typename recursive_get_transition_table<StateType>::type original_table; |
1142 | |
1143 | // we now look for the events the composite has in its internal transitions |
1144 | // the internal ones are searched recursively in sub-sub... states |
1145 | // we go recursively because our states can also have internal tables or substates etc. |
1146 | typedef typename recursive_get_internal_transition_table<StateType, ::boost::mpl::true_>::type recursive_istt; |
1147 | typedef typename ::boost::mpl::fold< |
1148 | recursive_istt,::boost::mpl::vector0<>, |
1149 | ::boost::mpl::push_back< ::boost::mpl::placeholders::_1, |
1150 | make_row_tag< ::boost::mpl::placeholders::_2 , StateType> > |
1151 | >::type recursive_istt_with_tag; |
1152 | |
1153 | typedef typename ::boost::mpl::insert_range< original_table, typename ::boost::mpl::end<original_table>::type, |
1154 | recursive_istt_with_tag>::type table_with_all_events; |
1155 | |
1156 | // and add for every event a forwarding row |
1157 | typedef typename ::boost::mpl::eval_if< |
1158 | typename CompilePolicy::add_forwarding_rows, |
1159 | add_forwarding_row_helper<table_with_all_events,::boost::mpl::vector0<>,StateType>, |
1160 | ::boost::mpl::identity< ::boost::mpl::vector0<> > |
1161 | >::type type; |
1162 | }; |
1163 | template <class StateType> |
1164 | struct get_internal_transition_table<StateType, ::boost::mpl::false_ > |
1165 | { |
1166 | typedef typename create_real_stt<StateType, typename StateType::internal_transition_table >::type type; |
1167 | }; |
1168 | // typedefs used internally |
1169 | typedef typename create_real_stt<Derived>::type real_transition_table; |
1170 | typedef typename create_stt<library_sm>::type stt; |
1171 | typedef typename get_initial_states<typename Derived::initial_state>::type initial_states; |
1172 | typedef typename generate_state_set<stt>::type state_list; |
1173 | typedef typename HistoryPolicy::template apply<nr_regions::value>::type concrete_history; |
1174 | |
1175 | typedef typename ::boost::fusion::result_of::as_set<state_list>::type substate_list; |
1176 | typedef typename ::boost::msm::back::generate_event_set< |
1177 | typename create_real_stt<library_sm, typename library_sm::internal_transition_table >::type |
1178 | >::type processable_events_internal_table; |
1179 | |
1180 | // extends the transition table with rows from composite states |
1181 | template <class Composite> |
1182 | struct extend_table |
1183 | { |
1184 | // add the init states |
1185 | //typedef typename create_stt<Composite>::type stt; |
1186 | typedef typename Composite::stt Stt; |
1187 | |
1188 | // add the internal events defined in the internal_transition_table |
1189 | // Note: these are added first because they must have a lesser prio |
1190 | // than the deeper transitions in the sub regions |
1191 | // table made of a stt + internal transitions of composite |
1192 | typedef typename ::boost::mpl::fold< |
1193 | typename Composite::internal_transition_table,::boost::mpl::vector0<>, |
1194 | ::boost::mpl::push_back< ::boost::mpl::placeholders::_1, |
1195 | make_row_tag< ::boost::mpl::placeholders::_2 , Composite> > |
1196 | >::type internal_stt; |
1197 | |
1198 | typedef typename ::boost::mpl::insert_range< |
1199 | Stt, |
1200 | typename ::boost::mpl::end<Stt>::type, |
1201 | internal_stt |
1202 | //typename get_internal_transition_table<Composite, ::boost::mpl::true_ >::type |
1203 | >::type stt_plus_internal; |
1204 | |
1205 | // for every state, add its transition table (if any) |
1206 | // transformed as frow |
1207 | typedef typename ::boost::mpl::fold<state_list,stt_plus_internal, |
1208 | ::boost::mpl::insert_range< |
1209 | ::boost::mpl::placeholders::_1, |
1210 | ::boost::mpl::end< ::boost::mpl::placeholders::_1>, |
1211 | get_internal_transition_table< |
1212 | ::boost::mpl::placeholders::_2, |
1213 | is_composite_state< ::boost::mpl::placeholders::_2> > > |
1214 | >::type type; |
1215 | }; |
1216 | // extend the table with tables from composite states |
1217 | typedef typename extend_table<library_sm>::type complete_table; |
1218 | // build a sequence of regions |
1219 | typedef typename get_regions_as_sequence<typename Derived::initial_state>::type seq_initial_states; |
1220 | // Member functions |
1221 | |
1222 | // start the state machine (calls entry of the initial state) |
1223 | void start() |
1224 | { |
1225 | // reinitialize our list of currently active states with the ones defined in Derived::initial_state |
1226 | ::boost::mpl::for_each< seq_initial_states, ::boost::msm::wrap<mpl::placeholders::_1> > |
1227 | (init_states(m_states)); |
1228 | // call on_entry on this SM |
1229 | (static_cast<Derived*>(this))->on_entry(fsm_initial_event(),*this); |
1230 | ::boost::mpl::for_each<initial_states, boost::msm::wrap<mpl::placeholders::_1> > |
1231 | (call_init<fsm_initial_event>(fsm_initial_event(),this)); |
1232 | // give a chance to handle an anonymous (eventless) transition |
1233 | handle_eventless_transitions_helper<library_sm> eventless_helper(this,true); |
1234 | eventless_helper.process_completion_event(); |
1235 | } |
1236 | |
1237 | // start the state machine (calls entry of the initial state passing incomingEvent to on_entry's) |
1238 | template <class Event> |
1239 | void start(Event const& incomingEvent) |
1240 | { |
1241 | // reinitialize our list of currently active states with the ones defined in Derived::initial_state |
1242 | ::boost::mpl::for_each< seq_initial_states, ::boost::msm::wrap<mpl::placeholders::_1> > |
1243 | (init_states(m_states)); |
1244 | // call on_entry on this SM |
1245 | (static_cast<Derived*>(this))->on_entry(incomingEvent,*this); |
1246 | ::boost::mpl::for_each<initial_states, boost::msm::wrap<mpl::placeholders::_1> > |
1247 | (call_init<Event>(incomingEvent,this)); |
1248 | // give a chance to handle an anonymous (eventless) transition |
1249 | handle_eventless_transitions_helper<library_sm> eventless_helper(this,true); |
1250 | eventless_helper.process_completion_event(); |
1251 | } |
1252 | |
1253 | // stop the state machine (calls exit of the current state) |
1254 | void stop() |
1255 | { |
1256 | do_exit(fsm_final_event(),*this); |
1257 | } |
1258 | |
1259 | // stop the state machine (calls exit of the current state passing finalEvent to on_exit's) |
1260 | template <class Event> |
1261 | void stop(Event const& finalEvent) |
1262 | { |
1263 | do_exit(finalEvent,*this); |
1264 | } |
1265 | |
1266 | // Main function used by clients of the derived FSM to make transitions. |
1267 | template<class Event> |
1268 | execute_return process_event(Event const& evt) |
1269 | { |
1270 | return process_event_internal(evt, EVENT_SOURCE_DIRECT); |
1271 | } |
1272 | |
1273 | template <class EventType> |
1274 | void enqueue_event_helper(EventType const& evt, ::boost::mpl::false_ const &) |
1275 | { |
1276 | execute_return (library_sm::*pf) (EventType const&, EventSource) = |
1277 | &library_sm::process_event_internal; |
1278 | |
1279 | m_events_queue.m_events_queue.push_back( |
1280 | ::boost::bind( |
1281 | pf, this, evt, |
1282 | static_cast<EventSource>(EVENT_SOURCE_MSG_QUEUE))); |
1283 | } |
1284 | template <class EventType> |
1285 | void enqueue_event_helper(EventType const& , ::boost::mpl::true_ const &) |
1286 | { |
1287 | // no queue |
1288 | } |
1289 | |
1290 | void execute_queued_events_helper(::boost::mpl::false_ const &) |
1291 | { |
1292 | while(!m_events_queue.m_events_queue.empty()) |
1293 | { |
1294 | transition_fct to_call = m_events_queue.m_events_queue.front(); |
1295 | m_events_queue.m_events_queue.pop_front(); |
1296 | to_call(); |
1297 | } |
1298 | } |
1299 | void execute_queued_events_helper(::boost::mpl::true_ const &) |
1300 | { |
1301 | // no queue required |
1302 | } |
1303 | void execute_single_queued_event_helper(::boost::mpl::false_ const &) |
1304 | { |
1305 | transition_fct to_call = m_events_queue.m_events_queue.front(); |
1306 | m_events_queue.m_events_queue.pop_front(); |
1307 | to_call(); |
1308 | } |
1309 | void execute_single_queued_event_helper(::boost::mpl::true_ const &) |
1310 | { |
1311 | // no queue required |
1312 | } |
1313 | // enqueues an event in the message queue |
1314 | // call execute_queued_events to process all queued events. |
1315 | // Be careful if you do this during event processing, the event will be processed immediately |
1316 | // and not kept in the queue |
1317 | template <class EventType> |
1318 | void enqueue_event(EventType const& evt) |
1319 | { |
1320 | enqueue_event_helper<EventType>(evt, typename is_no_message_queue<library_sm>::type()); |
1321 | } |
1322 | |
1323 | // empty the queue and process events |
1324 | void execute_queued_events() |
1325 | { |
1326 | execute_queued_events_helper(typename is_no_message_queue<library_sm>::type()); |
1327 | } |
1328 | void execute_single_queued_event() |
1329 | { |
1330 | execute_single_queued_event_helper(typename is_no_message_queue<library_sm>::type()); |
1331 | } |
1332 | typename events_queue_t::size_type get_message_queue_size() const |
1333 | { |
1334 | return m_events_queue.m_events_queue.size(); |
1335 | } |
1336 | |
1337 | events_queue_t& get_message_queue() |
1338 | { |
1339 | return m_events_queue.m_events_queue; |
1340 | } |
1341 | |
1342 | const events_queue_t& get_message_queue() const |
1343 | { |
1344 | return m_events_queue.m_events_queue; |
1345 | } |
1346 | |
1347 | void clear_deferred_queue() |
1348 | { |
1349 | m_deferred_events_queue.clear(); |
1350 | } |
1351 | |
1352 | deferred_events_queue_t& get_deferred_queue() |
1353 | { |
1354 | return m_deferred_events_queue.m_deferred_events_queue; |
1355 | } |
1356 | |
1357 | const deferred_events_queue_t& get_deferred_queue() const |
1358 | { |
1359 | return m_deferred_events_queue.m_deferred_events_queue; |
1360 | } |
1361 | |
1362 | // Getter that returns the current state of the FSM |
1363 | const int* current_state() const |
1364 | { |
1365 | return this->m_states; |
1366 | } |
1367 | |
1368 | template <class Archive> |
1369 | struct serialize_state |
1370 | { |
1371 | serialize_state(Archive& ar):ar_(ar){} |
1372 | |
1373 | template<typename T> |
1374 | typename ::boost::enable_if< |
1375 | typename ::boost::mpl::or_< |
1376 | typename has_do_serialize<T>::type, |
1377 | typename is_composite_state<T>::type |
1378 | >::type |
1379 | ,void |
1380 | >::type |
1381 | operator()(T& t) const |
1382 | { |
1383 | ar_ & t; |
1384 | } |
1385 | template<typename T> |
1386 | typename ::boost::disable_if< |
1387 | typename ::boost::mpl::or_< |
1388 | typename has_do_serialize<T>::type, |
1389 | typename is_composite_state<T>::type |
1390 | >::type |
1391 | ,void |
1392 | >::type |
1393 | operator()(T&) const |
1394 | { |
1395 | // no state to serialize |
1396 | } |
1397 | Archive& ar_; |
1398 | }; |
1399 | |
1400 | template<class Archive> |
1401 | void serialize(Archive & ar, const unsigned int) |
1402 | { |
1403 | // invoke serialization of the base class |
1404 | (serialize_state<Archive>(ar))(boost::serialization::base_object<Derived>(*this)); |
1405 | // now our attributes |
1406 | ar & m_states; |
1407 | // queues cannot be serialized => skip |
1408 | ar & m_history; |
1409 | ar & m_event_processing; |
1410 | ar & m_is_included; |
1411 | // visitors cannot be serialized => skip |
1412 | ::boost::fusion::for_each(m_substate_list, serialize_state<Archive>(ar)); |
1413 | } |
1414 | |
1415 | // linearly search for the state with the given id |
1416 | struct get_state_id_helper |
1417 | { |
1418 | get_state_id_helper(int id,const BaseState** res,const library_sm* self_): |
1419 | result_state(res),searched_id(id),self(self_) {} |
1420 | |
1421 | template <class StateType> |
1422 | void operator()(boost::msm::wrap<StateType> const&) |
1423 | { |
1424 | // look for the state id until found |
1425 | BOOST_STATIC_CONSTANT(int, id = (get_state_id<stt,StateType>::value)); |
1426 | if (!*result_state && (id == searched_id)) |
1427 | { |
1428 | *result_state = &::boost::fusion::at_key<StateType>(self->m_substate_list); |
1429 | } |
1430 | } |
1431 | const BaseState** result_state; |
1432 | int searched_id; |
1433 | const library_sm* self; |
1434 | }; |
1435 | // return the state whose id is passed or 0 if not found |
1436 | // caution if you need this, you probably need polymorphic states |
1437 | // complexity: O(number of states) |
1438 | BaseState* get_state_by_id(int id) |
1439 | { |
1440 | const BaseState* result_state=0; |
1441 | ::boost::mpl::for_each<state_list, |
1442 | ::boost::msm::wrap< ::boost::mpl::placeholders::_1> > (get_state_id_helper(id,&result_state,this)); |
1443 | return const_cast<BaseState*>(result_state); |
1444 | } |
1445 | const BaseState* get_state_by_id(int id) const |
1446 | { |
1447 | const BaseState* result_state=0; |
1448 | ::boost::mpl::for_each<state_list, |
1449 | ::boost::msm::wrap< ::boost::mpl::placeholders::_1> > (get_state_id_helper(id,&result_state,this)); |
1450 | return result_state; |
1451 | } |
1452 | // true if the sm is used in another sm |
1453 | bool is_contained() const |
1454 | { |
1455 | return m_is_included; |
1456 | } |
1457 | // get the history policy class |
1458 | concrete_history& get_history() |
1459 | { |
1460 | return m_history; |
1461 | } |
1462 | concrete_history const& get_history() const |
1463 | { |
1464 | return m_history; |
1465 | } |
1466 | // get a state (const version) |
1467 | // as a pointer |
1468 | template <class State> |
1469 | typename ::boost::enable_if<typename ::boost::is_pointer<State>::type,State >::type |
1470 | get_state(::boost::msm::back::dummy<0> = 0) const |
1471 | { |
1472 | return const_cast<State > |
1473 | (& |
1474 | (::boost::fusion::at_key< |
1475 | typename ::boost::remove_const<typename ::boost::remove_pointer<State>::type>::type>(m_substate_list))); |
1476 | } |
1477 | // as a reference |
1478 | template <class State> |
1479 | typename ::boost::enable_if<typename ::boost::is_reference<State>::type,State >::type |
1480 | get_state(::boost::msm::back::dummy<1> = 0) const |
1481 | { |
1482 | return const_cast<State > |
1483 | ( ::boost::fusion::at_key< |
1484 | typename ::boost::remove_const<typename ::boost::remove_reference<State>::type>::type>(m_substate_list) ); |
1485 | } |
1486 | // get a state (non const version) |
1487 | // as a pointer |
1488 | template <class State> |
1489 | typename ::boost::enable_if<typename ::boost::is_pointer<State>::type,State >::type |
1490 | get_state(::boost::msm::back::dummy<0> = 0) |
1491 | { |
1492 | return &(static_cast<typename boost::add_reference<typename ::boost::remove_pointer<State>::type>::type > |
1493 | (::boost::fusion::at_key<typename ::boost::remove_pointer<State>::type>(m_substate_list))); |
1494 | } |
1495 | // as a reference |
1496 | template <class State> |
1497 | typename ::boost::enable_if<typename ::boost::is_reference<State>::type,State >::type |
1498 | get_state(::boost::msm::back::dummy<1> = 0) |
1499 | { |
1500 | return ::boost::fusion::at_key<typename ::boost::remove_reference<State>::type>(m_substate_list); |
1501 | } |
1502 | // checks if a flag is active using the BinaryOp as folding function |
1503 | template <class Flag,class BinaryOp> |
1504 | bool is_flag_active() const |
1505 | { |
1506 | flag_handler* flags_entries = get_entries_for_flag<Flag>(); |
1507 | bool res = (*flags_entries[ m_states[0] ])(*this); |
1508 | for (int i = 1; i < nr_regions::value ; ++i) |
1509 | { |
1510 | res = typename BinaryOp::type() (res,(*flags_entries[ m_states[i] ])(*this)); |
1511 | } |
1512 | return res; |
1513 | } |
1514 | // checks if a flag is active using no binary op if 1 region, or OR if > 1 regions |
1515 | template <class Flag> |
1516 | bool is_flag_active() const |
1517 | { |
1518 | return FlagHelper<Flag,(nr_regions::value>1)>::helper(*this,get_entries_for_flag<Flag>()); |
1519 | } |
1520 | // visit the currently active states (if these are defined as visitable |
1521 | // by implementing accept) |
1522 | void visit_current_states() |
1523 | { |
1524 | for (int i=0; i<nr_regions::value;++i) |
1525 | { |
1526 | m_visitors.execute(m_states[i]); |
1527 | } |
1528 | } |
1529 | #define MSM_VISIT_STATE_SUB(z, n, unused) ARG ## n vis ## n |
1530 | #define MSM_VISIT_STATE_EXECUTE(z, n, unused) \ |
1531 | template <BOOST_PP_ENUM_PARAMS(n, class ARG)> \ |
1532 | void visit_current_states(BOOST_PP_ENUM(n, MSM_VISIT_STATE_SUB, ~ ) ) \ |
1533 | { \ |
1534 | for (int i=0; i<nr_regions::value;++i) \ |
1535 | { \ |
1536 | m_visitors.execute(m_states[i],BOOST_PP_ENUM_PARAMS(n,vis)); \ |
1537 | } \ |
1538 | } |
1539 | BOOST_PP_REPEAT_FROM_TO(1,BOOST_PP_ADD(BOOST_MSM_VISITOR_ARG_SIZE,1), MSM_VISIT_STATE_EXECUTE, ~) |
1540 | #undef MSM_VISIT_STATE_EXECUTE |
1541 | #undef MSM_VISIT_STATE_SUB |
1542 | |
1543 | // puts the given event into the deferred queue |
1544 | template <class Event> |
1545 | typename::boost::disable_if< typename ::boost::msm::is_kleene_event<Event>::type, void>::type |
1546 | defer_event(Event const& e) |
1547 | { |
1548 | // to call this function, you need either a state with a deferred_events typedef |
1549 | // or that the fsm provides the activate_deferred_events typedef |
1550 | BOOST_MPL_ASSERT(( has_fsm_deferred_events<library_sm> )); |
1551 | execute_return (library_sm::*pf) (Event const&, EventSource) = |
1552 | &library_sm::process_event_internal; |
1553 | |
1554 | // Deferred events are added with a correlation sequence that helps to |
1555 | // identify when an event was added - This is typically to distinguish |
1556 | // between events deferred in this processing versus previous. |
1557 | m_deferred_events_queue.m_deferred_events_queue.push_back( |
1558 | std::make_pair( |
1559 | ::boost::bind( |
1560 | pf, this, e, static_cast<EventSource>(EVENT_SOURCE_DIRECT|EVENT_SOURCE_DEFERRED)), |
1561 | static_cast<char>(m_deferred_events_queue.m_cur_seq+1))); |
1562 | } |
1563 | protected: |
1564 | template <class KleeneEvent, class Fsm> |
1565 | struct defer_event_kleene_helper |
1566 | { |
1567 | defer_event_kleene_helper(KleeneEvent const& e, Fsm* fsm, bool& found) |
1568 | : m_event(e), m_fsm(fsm), m_found(found) {} |
1569 | |
1570 | // History initializer function object, used with mpl::for_each |
1571 | template <class Event> |
1572 | void operator()(Event const& ev) |
1573 | { |
1574 | if (m_event.type() == boost::typeindex::type_id<decltype(ev)>().type_info()) |
1575 | { |
1576 | m_found = true; |
1577 | // to call this function, you need either a state with a deferred_events typedef |
1578 | // or that the fsm provides the activate_deferred_events typedef |
1579 | BOOST_MPL_ASSERT((has_fsm_deferred_events<library_sm>)); |
1580 | ::boost::msm::back::execute_return(library_sm:: * pf) (Event const&, ::boost::msm::back::EventSource) = |
1581 | &library_sm::process_event_internal; |
1582 | |
1583 | // Deferred events are added with a correlation sequence that helps to |
1584 | // identify when an event was added - This is typically to distinguish |
1585 | // between events deferred in this processing versus previous. |
1586 | m_fsm->m_deferred_events_queue.m_deferred_events_queue.push_back( |
1587 | std::make_pair( |
1588 | ::boost::bind( |
1589 | pf, m_fsm, boost::any_cast<Event>(m_event), static_cast<::boost::msm::back::EventSource>(::boost::msm::back::EVENT_SOURCE_DIRECT | ::boost::msm::back::EVENT_SOURCE_DEFERRED)), |
1590 | static_cast<char>(m_fsm->m_deferred_events_queue.m_cur_seq + 1))); |
1591 | } |
1592 | } |
1593 | KleeneEvent const& m_event; |
1594 | Fsm* m_fsm; |
1595 | bool& m_found; |
1596 | }; |
1597 | |
1598 | public: |
1599 | template <class Event> |
1600 | typename::boost::enable_if< typename ::boost::msm::is_kleene_event<Event>::type, void>::type |
1601 | defer_event(Event const& e) |
1602 | { |
1603 | typedef typename generate_event_set<stt>::type event_list; |
1604 | bool found = false; |
1605 | boost::fusion::for_each( |
1606 | event_list(), |
1607 | defer_event_kleene_helper<Event,library_sm>(e,this,found)); |
1608 | if (!found) |
1609 | { |
1610 | for (int i = 0; i < nr_regions::value; ++i) |
1611 | { |
1612 | this->no_transition(e, *this, this->m_states[i]); |
1613 | } |
1614 | } |
1615 | } |
1616 | |
1617 | |
1618 | protected: // interface for the derived class |
1619 | |
1620 | // helper used to fill the initial states |
1621 | struct init_states |
1622 | { |
1623 | init_states(int* const init):m_initial_states(init),m_index(-1){} |
1624 | |
1625 | // History initializer function object, used with mpl::for_each |
1626 | template <class State> |
1627 | void operator()(::boost::msm::wrap<State> const&) |
1628 | { |
1629 | m_initial_states[++m_index]=get_state_id<stt,State>::type::value; |
1630 | } |
1631 | int* const m_initial_states; |
1632 | int m_index; |
1633 | }; |
1634 | public: |
1635 | struct update_state |
1636 | { |
1637 | update_state(substate_list& to_overwrite_):to_overwrite(&to_overwrite_){} |
1638 | template<typename StateType> |
1639 | void operator()(StateType const& astate) const |
1640 | { |
1641 | ::boost::fusion::at_key<StateType>(*to_overwrite)=astate; |
1642 | } |
1643 | substate_list* to_overwrite; |
1644 | }; |
1645 | template <class Expr> |
1646 | void set_states(Expr const& expr) |
1647 | { |
1648 | ::boost::fusion::for_each( |
1649 | ::boost::fusion::as_vector(FoldToList()(expr, boost::fusion::nil_())),update_state(this->m_substate_list)); |
1650 | } |
1651 | |
1652 | // Construct with the default initial states |
1653 | state_machine() |
1654 | :Derived() |
1655 | ,m_events_queue() |
1656 | ,m_deferred_events_queue() |
1657 | ,m_history() |
1658 | ,m_event_processing(false) |
1659 | ,m_is_included(false) |
1660 | ,m_visitors() |
1661 | ,m_substate_list() |
1662 | { |
1663 | // initialize our list of states with the ones defined in Derived::initial_state |
1664 | ::boost::mpl::for_each< seq_initial_states, ::boost::msm::wrap<mpl::placeholders::_1> > |
1665 | (init_states(m_states)); |
1666 | m_history.set_initial_states(m_states); |
1667 | // create states |
1668 | fill_states(this); |
1669 | } |
1670 | |
1671 | // Construct with the default initial states and some default argument(s) |
1672 | #if defined (BOOST_NO_CXX11_RVALUE_REFERENCES) \ |
1673 | || defined (BOOST_NO_CXX11_VARIADIC_TEMPLATES) \ |
1674 | || defined (BOOST_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS) |
1675 | template <class Expr> |
1676 | state_machine |
1677 | (Expr const& expr, typename ::boost::enable_if<typename ::boost::proto::is_expr<Expr>::type >::type* = 0) |
1678 | :Derived() |
1679 | , m_events_queue() |
1680 | , m_deferred_events_queue() |
1681 | , m_history() |
1682 | , m_event_processing(false) |
1683 | , m_is_included(false) |
1684 | , m_visitors() |
1685 | , m_substate_list() |
1686 | { |
1687 | BOOST_MPL_ASSERT_MSG( |
1688 | (::boost::proto::matches<Expr, FoldToList>::value), |
1689 | THE_STATES_EXPRESSION_PASSED_DOES_NOT_MATCH_GRAMMAR, |
1690 | (FoldToList)); |
1691 | |
1692 | // initialize our list of states with the ones defined in Derived::initial_state |
1693 | ::boost::mpl::for_each< seq_initial_states, ::boost::msm::wrap<mpl::placeholders::_1> > |
1694 | (init_states(m_states)); |
1695 | m_history.set_initial_states(m_states); |
1696 | // create states |
1697 | set_states(expr); |
1698 | fill_states(this); |
1699 | } |
1700 | #define MSM_CONSTRUCTOR_HELPER_EXECUTE_SUB(z, n, unused) ARG ## n t ## n |
1701 | #define MSM_CONSTRUCTOR_HELPER_EXECUTE(z, n, unused) \ |
1702 | template <BOOST_PP_ENUM_PARAMS(n, class ARG)> \ |
1703 | state_machine<A0,A1,A2,A3,A4 \ |
1704 | >(BOOST_PP_ENUM(n, MSM_CONSTRUCTOR_HELPER_EXECUTE_SUB, ~ ), \ |
1705 | typename ::boost::disable_if<typename ::boost::proto::is_expr<ARG0>::type >::type* =0 ) \ |
1706 | :Derived(BOOST_PP_ENUM_PARAMS(n,t)) \ |
1707 | ,m_events_queue() \ |
1708 | ,m_deferred_events_queue() \ |
1709 | ,m_history() \ |
1710 | ,m_event_processing(false) \ |
1711 | ,m_is_included(false) \ |
1712 | ,m_visitors() \ |
1713 | ,m_substate_list() \ |
1714 | { \ |
1715 | ::boost::mpl::for_each< seq_initial_states, ::boost::msm::wrap<mpl::placeholders::_1> > \ |
1716 | (init_states(m_states)); \ |
1717 | m_history.set_initial_states(m_states); \ |
1718 | fill_states(this); \ |
1719 | } \ |
1720 | template <class Expr,BOOST_PP_ENUM_PARAMS(n, class ARG)> \ |
1721 | state_machine<A0,A1,A2,A3,A4 \ |
1722 | >(Expr const& expr,BOOST_PP_ENUM(n, MSM_CONSTRUCTOR_HELPER_EXECUTE_SUB, ~ ), \ |
1723 | typename ::boost::enable_if<typename ::boost::proto::is_expr<Expr>::type >::type* =0 ) \ |
1724 | :Derived(BOOST_PP_ENUM_PARAMS(n,t)) \ |
1725 | ,m_events_queue() \ |
1726 | ,m_deferred_events_queue() \ |
1727 | ,m_history() \ |
1728 | ,m_event_processing(false) \ |
1729 | ,m_is_included(false) \ |
1730 | ,m_visitors() \ |
1731 | ,m_substate_list() \ |
1732 | { \ |
1733 | BOOST_MPL_ASSERT_MSG( \ |
1734 | ( ::boost::proto::matches<Expr, FoldToList>::value), \ |
1735 | THE_STATES_EXPRESSION_PASSED_DOES_NOT_MATCH_GRAMMAR, \ |
1736 | (FoldToList)); \ |
1737 | ::boost::mpl::for_each< seq_initial_states, ::boost::msm::wrap<mpl::placeholders::_1> > \ |
1738 | (init_states(m_states)); \ |
1739 | m_history.set_initial_states(m_states); \ |
1740 | set_states(expr); \ |
1741 | fill_states(this); \ |
1742 | } |
1743 | |
1744 | BOOST_PP_REPEAT_FROM_TO(1,BOOST_PP_ADD(BOOST_MSM_CONSTRUCTOR_ARG_SIZE,1), MSM_CONSTRUCTOR_HELPER_EXECUTE, ~) |
1745 | #undef MSM_CONSTRUCTOR_HELPER_EXECUTE |
1746 | #undef MSM_CONSTRUCTOR_HELPER_EXECUTE_SUB |
1747 | |
1748 | #else |
1749 | template <class ARG0,class... ARG,class=typename ::boost::disable_if<typename ::boost::proto::is_expr<ARG0>::type >::type> |
1750 | state_machine(ARG0&& t0,ARG&&... t) |
1751 | :Derived(std::forward<ARG0>(t0), std::forward<ARG>(t)...) |
1752 | ,m_events_queue() |
1753 | ,m_deferred_events_queue() |
1754 | ,m_history() |
1755 | ,m_event_processing(false) |
1756 | ,m_is_included(false) |
1757 | ,m_visitors() |
1758 | ,m_substate_list() |
1759 | { |
1760 | ::boost::mpl::for_each< seq_initial_states, ::boost::msm::wrap<mpl::placeholders::_1> > |
1761 | (init_states(m_states)); |
1762 | m_history.set_initial_states(m_states); |
1763 | fill_states(this); |
1764 | } |
1765 | template <class Expr,class... ARG,class=typename ::boost::enable_if<typename ::boost::proto::is_expr<Expr>::type >::type> |
1766 | state_machine(Expr const& expr,ARG&&... t) |
1767 | :Derived(std::forward<ARG>(t)...) |
1768 | ,m_events_queue() |
1769 | ,m_deferred_events_queue() |
1770 | ,m_history() |
1771 | ,m_event_processing(false) |
1772 | ,m_is_included(false) |
1773 | ,m_visitors() |
1774 | ,m_substate_list() |
1775 | { |
1776 | BOOST_MPL_ASSERT_MSG( |
1777 | ( ::boost::proto::matches<Expr, FoldToList>::value), |
1778 | THE_STATES_EXPRESSION_PASSED_DOES_NOT_MATCH_GRAMMAR, |
1779 | (FoldToList)); |
1780 | ::boost::mpl::for_each< seq_initial_states, ::boost::msm::wrap<mpl::placeholders::_1> > |
1781 | (init_states(m_states)); |
1782 | m_history.set_initial_states(m_states); |
1783 | set_states(expr); |
1784 | fill_states(this); |
1785 | } |
1786 | #endif |
1787 | |
1788 | |
1789 | // assignment operator using the copy policy to decide if non_copyable, shallow or deep copying is necessary |
1790 | library_sm& operator= (library_sm const& rhs) |
1791 | { |
1792 | if (this != &rhs) |
1793 | { |
1794 | Derived::operator=(rhs); |
1795 | do_copy(rhs); |
1796 | } |
1797 | return *this; |
1798 | } |
1799 | state_machine(library_sm const& rhs) |
1800 | : Derived(rhs) |
1801 | { |
1802 | if (this != &rhs) |
1803 | { |
1804 | // initialize our list of states with the ones defined in Derived::initial_state |
1805 | fill_states(this); |
1806 | do_copy(rhs); |
1807 | } |
1808 | } |
1809 | |
1810 | // the following 2 functions handle the terminate/interrupt states handling |
1811 | // if one of these states is found, the first one is used |
1812 | template <class Event> |
1813 | bool is_event_handling_blocked_helper( ::boost::mpl::true_ const &) |
1814 | { |
1815 | // if the state machine is terminated, do not handle any event |
1816 | if (is_flag_active< ::boost::msm::TerminateFlag>()) |
1817 | return true; |
1818 | // if the state machine is interrupted, do not handle any event |
1819 | // unless the event is the end interrupt event |
1820 | if ( is_flag_active< ::boost::msm::InterruptedFlag>() && |
1821 | !is_flag_active< ::boost::msm::EndInterruptFlag<Event> >()) |
1822 | return true; |
1823 | return false; |
1824 | } |
1825 | // otherwise simple handling, no flag => continue |
1826 | template <class Event> |
1827 | bool is_event_handling_blocked_helper( ::boost::mpl::false_ const &) |
1828 | { |
1829 | // no terminate/interrupt states detected |
1830 | return false; |
1831 | } |
1832 | void do_handle_prio_msg_queue_deferred_queue(EventSource source, HandledEnum handled, ::boost::mpl::true_ const &) |
1833 | { |
1834 | // non-default. Handle msg queue with higher prio than deferred queue |
1835 | if (!(EVENT_SOURCE_MSG_QUEUE & source)) |
1836 | { |
1837 | do_post_msg_queue_helper( |
1838 | ::boost::mpl::bool_< |
1839 | is_no_message_queue<library_sm>::type::value>()); |
1840 | if (!(EVENT_SOURCE_DEFERRED & source)) |
1841 | { |
1842 | handle_defer_helper<library_sm> defer_helper(m_deferred_events_queue); |
1843 | defer_helper.do_handle_deferred(HANDLED_TRUE & handled); |
1844 | } |
1845 | } |
1846 | } |
1847 | void do_handle_prio_msg_queue_deferred_queue(EventSource source, HandledEnum handled, ::boost::mpl::false_ const &) |
1848 | { |
1849 | // default. Handle deferred queue with higher prio than msg queue |
1850 | if (!(EVENT_SOURCE_DEFERRED & source)) |
1851 | { |
1852 | handle_defer_helper<library_sm> defer_helper(m_deferred_events_queue); |
1853 | defer_helper.do_handle_deferred(HANDLED_TRUE & handled); |
1854 | |
1855 | // Handle any new events generated into the queue, but only if |
1856 | // we're not already processing from the message queue. |
1857 | if (!(EVENT_SOURCE_MSG_QUEUE & source)) |
1858 | { |
1859 | do_post_msg_queue_helper( |
1860 | ::boost::mpl::bool_< |
1861 | is_no_message_queue<library_sm>::type::value>()); |
1862 | } |
1863 | } |
1864 | } |
1865 | // the following functions handle pre/post-process handling of a message queue |
1866 | template <class StateType,class EventType> |
1867 | bool do_pre_msg_queue_helper(EventType const&, ::boost::mpl::true_ const &) |
1868 | { |
1869 | // no message queue needed |
1870 | return true; |
1871 | } |
1872 | template <class StateType,class EventType> |
1873 | bool do_pre_msg_queue_helper(EventType const& evt, ::boost::mpl::false_ const &) |
1874 | { |
1875 | execute_return (library_sm::*pf) (EventType const&, EventSource) = |
1876 | &library_sm::process_event_internal; |
1877 | |
1878 | // if we are already processing an event |
1879 | if (m_event_processing) |
1880 | { |
1881 | // event has to be put into the queue |
1882 | m_events_queue.m_events_queue.push_back( |
1883 | ::boost::bind( |
1884 | pf, this, evt, |
1885 | static_cast<EventSource>(EVENT_SOURCE_DIRECT | EVENT_SOURCE_MSG_QUEUE))); |
1886 | |
1887 | return false; |
1888 | } |
1889 | |
1890 | // event can be handled, processing |
1891 | m_event_processing = true; |
1892 | return true; |
1893 | } |
1894 | void do_post_msg_queue_helper( ::boost::mpl::true_ const &) |
1895 | { |
1896 | // no message queue needed |
1897 | } |
1898 | void do_post_msg_queue_helper( ::boost::mpl::false_ const &) |
1899 | { |
1900 | process_message_queue(this); |
1901 | } |
1902 | void do_allow_event_processing_after_transition( ::boost::mpl::true_ const &) |
1903 | { |
1904 | // no message queue needed |
1905 | } |
1906 | void do_allow_event_processing_after_transition( ::boost::mpl::false_ const &) |
1907 | { |
1908 | m_event_processing = false; |
1909 | } |
1910 | // the following 2 functions handle the processing either with a try/catch protection or without |
1911 | template <class StateType,class EventType> |
1912 | HandledEnum do_process_helper(EventType const& evt, ::boost::mpl::true_ const &, bool is_direct_call) |
1913 | { |
1914 | return this->do_process_event(evt,is_direct_call); |
1915 | } |
1916 | template <class StateType,class EventType> |
1917 | HandledEnum do_process_helper(EventType const& evt, ::boost::mpl::false_ const &, bool is_direct_call) |
1918 | { |
1919 | // when compiling without exception support there is no formal parameter "e" in the catch handler. |
1920 | // Declaring a local variable here does not hurt and will be "used" to make the code in the handler |
1921 | // compilable although the code will never be executed. |
1922 | std::exception e; |
1923 | BOOST_TRY |
1924 | { |
1925 | return this->do_process_event(evt,is_direct_call); |
1926 | } |
1927 | BOOST_CATCH (std::exception& e) |
1928 | { |
1929 | // give a chance to the concrete state machine to handle |
1930 | this->exception_caught(evt,*this,e); |
1931 | return ::boost::msm::back::HANDLED_FALSE; |
1932 | } |
1933 | BOOST_CATCH_END |
1934 | return HANDLED_TRUE; |
1935 | } |
1936 | // handling of deferred events |
1937 | // if none is found in the SM, take the following empty main version |
1938 | template <class StateType, class Enable = int> |
1939 | struct handle_defer_helper |
1940 | { |
1941 | handle_defer_helper(deferred_msg_queue_helper<library_sm>& ){} |
1942 | void do_handle_deferred(bool) |
1943 | { |
1944 | } |
1945 | }; |
1946 | // otherwise the standard version handling the deferred events |
1947 | template <class StateType> |
1948 | struct handle_defer_helper |
1949 | <StateType, typename enable_if< typename ::boost::msm::back::has_fsm_deferred_events<StateType>::type,int >::type> |
1950 | { |
1951 | struct sort_greater |
1952 | { |
1953 | bool operator()( |
1954 | typename deferred_events_queue_t::value_type const& d1, |
1955 | typename deferred_events_queue_t::value_type const& d2) |
1956 | { |
1957 | return d1.second > d2.second; |
1958 | } |
1959 | }; |
1960 | struct set_sequence |
1961 | { |
1962 | set_sequence(char s) :seq_(s) {} |
1963 | void operator()(typename deferred_events_queue_t::value_type& d) |
1964 | { |
1965 | d.second = seq_; |
1966 | } |
1967 | char seq_; |
1968 | }; |
1969 | handle_defer_helper(deferred_msg_queue_helper<library_sm>& a_queue): |
1970 | m_events_queue(a_queue) {} |
1971 | void do_handle_deferred(bool new_seq=false) |
1972 | { |
1973 | // A new sequence is typically started upon initial entry to the |
1974 | // state, or upon a new transition. When this occurs we want to |
1975 | // process all previously deferred events by incrementing the |
1976 | // correlation sequence. |
1977 | if (new_seq) |
1978 | { |
1979 | ++m_events_queue.m_cur_seq; |
1980 | } |
1981 | |
1982 | char& cur_seq = m_events_queue.m_cur_seq; |
1983 | |
1984 | // Iteratively process all of the events within the deferred |
1985 | // queue upto (but not including) newly deferred events. |
1986 | // if we did not defer one in the queue, then we need to try again |
1987 | bool not_only_deferred = false; |
1988 | while (!m_events_queue.m_deferred_events_queue.empty()) |
1989 | { |
1990 | typename deferred_events_queue_t::value_type& pair = |
1991 | m_events_queue.m_deferred_events_queue.front(); |
1992 | |
1993 | if (cur_seq != pair.second) |
1994 | { |
1995 | break; |
1996 | } |
1997 | |
1998 | deferred_fct next = pair.first; |
1999 | m_events_queue.m_deferred_events_queue.pop_front(); |
2000 | boost::msm::back::execute_return res = next(); |
2001 | if (res != ::boost::msm::back::HANDLED_FALSE && res != ::boost::msm::back::HANDLED_DEFERRED) |
2002 | { |
2003 | not_only_deferred = true; |
2004 | } |
2005 | if (not_only_deferred) |
2006 | { |
2007 | // handled one, stop processing deferred until next block reorders |
2008 | break; |
2009 | } |
2010 | } |
2011 | if (not_only_deferred) |
2012 | { |
2013 | // attempt to go back to the situation prior to processing, |
2014 | // in case some deferred events would have been re-queued |
2015 | // in that case those would have a higher sequence number |
2016 | std::stable_sort( |
2017 | m_events_queue.m_deferred_events_queue.begin(), |
2018 | m_events_queue.m_deferred_events_queue.end(), |
2019 | sort_greater() |
2020 | ); |
2021 | // reset sequence number for all |
2022 | std::for_each( |
2023 | m_events_queue.m_deferred_events_queue.begin(), |
2024 | m_events_queue.m_deferred_events_queue.end(), |
2025 | set_sequence(m_events_queue.m_cur_seq + 1) |
2026 | ); |
2027 | // one deferred event was successfully processed, try again |
2028 | do_handle_deferred(new_seq: true); |
2029 | } |
2030 | } |
2031 | |
2032 | private: |
2033 | deferred_msg_queue_helper<library_sm>& m_events_queue; |
2034 | }; |
2035 | |
2036 | // handling of eventless transitions |
2037 | // if none is found in the SM, nothing to do |
2038 | template <class StateType, class Enable = void> |
2039 | struct handle_eventless_transitions_helper |
2040 | { |
2041 | handle_eventless_transitions_helper(library_sm* , bool ){} |
2042 | void process_completion_event(EventSource = EVENT_SOURCE_DEFAULT){} |
2043 | }; |
2044 | // otherwise |
2045 | template <class StateType> |
2046 | struct handle_eventless_transitions_helper |
2047 | <StateType, typename enable_if< typename ::boost::msm::back::has_fsm_eventless_transition<StateType>::type >::type> |
2048 | { |
2049 | handle_eventless_transitions_helper(library_sm* self_, bool handled_):self(self_),handled(handled_){} |
2050 | void process_completion_event(EventSource source = EVENT_SOURCE_DEFAULT) |
2051 | { |
2052 | typedef typename ::boost::mpl::deref< |
2053 | typename ::boost::mpl::begin< |
2054 | typename find_completion_events<StateType>::type |
2055 | >::type |
2056 | >::type first_completion_event; |
2057 | if (handled) |
2058 | { |
2059 | self->process_event_internal( |
2060 | first_completion_event(), |
2061 | source | EVENT_SOURCE_DIRECT); |
2062 | } |
2063 | } |
2064 | |
2065 | private: |
2066 | library_sm* self; |
2067 | bool handled; |
2068 | }; |
2069 | |
2070 | // helper class called in case the event to process has been found in the fsm's internal stt and is therefore processable |
2071 | template<class Event> |
2072 | struct process_fsm_internal_table |
2073 | { |
2074 | typedef typename ::boost::mpl::has_key<processable_events_internal_table,Event>::type is_event_processable; |
2075 | |
2076 | // forward to the correct do_process |
2077 | static void process(Event const& evt,library_sm* self_,HandledEnum& result) |
2078 | { |
2079 | do_process(evt,self_,result,is_event_processable()); |
2080 | } |
2081 | private: |
2082 | // the event is processable, let's try! |
2083 | static void do_process(Event const& evt,library_sm* self_,HandledEnum& result, ::boost::mpl::true_) |
2084 | { |
2085 | if (result != HANDLED_TRUE) |
2086 | { |
2087 | typedef dispatch_table<library_sm,complete_table,Event,CompilePolicy> table; |
2088 | HandledEnum res_internal = table::instance().entries[0](*self_, 0, self_->m_states[0], evt); |
2089 | result = (HandledEnum)((int)result | (int)res_internal); |
2090 | } |
2091 | } |
2092 | // version doing nothing if the event is not in the internal stt and we can save ourselves the time trying to process |
2093 | static void do_process(Event const& ,library_sm* ,HandledEnum& , ::boost::mpl::false_) |
2094 | { |
2095 | // do nothing |
2096 | } |
2097 | }; |
2098 | |
2099 | template <class StateType,class Enable=void> |
2100 | struct region_processing_helper |
2101 | { |
2102 | public: |
2103 | region_processing_helper(library_sm* self_,HandledEnum& result_) |
2104 | :self(self_),result(result_){} |
2105 | template<class Event> |
2106 | void process(Event const& evt) |
2107 | { |
2108 | // use this table as if it came directly from the user |
2109 | typedef dispatch_table<library_sm,complete_table,Event,CompilePolicy> table; |
2110 | // +1 because index 0 is reserved for this fsm |
2111 | HandledEnum res = |
2112 | table::instance().entries[self->m_states[0]+1]( |
2113 | *self, 0, self->m_states[0], evt); |
2114 | result = (HandledEnum)((int)result | (int)res); |
2115 | // process the event in the internal table of this fsm if the event is processable (present in the table) |
2116 | process_fsm_internal_table<Event>::process(evt,self,result); |
2117 | } |
2118 | library_sm* self; |
2119 | HandledEnum& result; |
2120 | }; |
2121 | // version with visitors |
2122 | template <class StateType> |
2123 | struct region_processing_helper<StateType,typename ::boost::enable_if< |
2124 | ::boost::mpl::is_sequence<typename StateType::initial_state> >::type> |
2125 | { |
2126 | private: |
2127 | // process event in one region |
2128 | template <class region_id,int Dummy=0> |
2129 | struct In |
2130 | { |
2131 | template<class Event> |
2132 | static void process(Event const& evt,library_sm* self_,HandledEnum& result_) |
2133 | { |
2134 | // use this table as if it came directly from the user |
2135 | typedef dispatch_table<library_sm,complete_table,Event,CompilePolicy> table; |
2136 | // +1 because index 0 is reserved for this fsm |
2137 | HandledEnum res = |
2138 | table::instance().entries[self_->m_states[region_id::value]+1]( |
2139 | *self_, region_id::value , self_->m_states[region_id::value], evt); |
2140 | result_ = (HandledEnum)((int)result_ | (int)res); |
2141 | In< ::boost::mpl::int_<region_id::value+1> >::process(evt,self_,result_); |
2142 | } |
2143 | }; |
2144 | template <int Dummy> |
2145 | struct In< ::boost::mpl::int_<nr_regions::value>,Dummy> |
2146 | { |
2147 | // end of processing |
2148 | template<class Event> |
2149 | static void process(Event const& evt,library_sm* self_,HandledEnum& result_) |
2150 | { |
2151 | // process the event in the internal table of this fsm if the event is processable (present in the table) |
2152 | process_fsm_internal_table<Event>::process(evt,self_,result_); |
2153 | } |
2154 | }; |
2155 | public: |
2156 | region_processing_helper(library_sm* self_,HandledEnum& result_) |
2157 | :self(self_),result(result_){} |
2158 | template<class Event> |
2159 | void process(Event const& evt) |
2160 | { |
2161 | In< ::boost::mpl::int_<0> >::process(evt,self,result); |
2162 | } |
2163 | |
2164 | library_sm* self; |
2165 | HandledEnum& result; |
2166 | }; |
2167 | |
2168 | // Main function used internally to make transitions |
2169 | // Can only be called for internally (for example in an action method) generated events. |
2170 | template<class Event> |
2171 | execute_return process_event_internal(Event const& evt, |
2172 | EventSource source = EVENT_SOURCE_DEFAULT) |
2173 | { |
2174 | // if the state machine has terminate or interrupt flags, check them, otherwise skip |
2175 | if (is_event_handling_blocked_helper<Event> |
2176 | ( ::boost::mpl::bool_<has_fsm_blocking_states<library_sm>::type::value>() ) ) |
2177 | { |
2178 | return HANDLED_TRUE; |
2179 | } |
2180 | |
2181 | // if a message queue is needed and processing is on the way |
2182 | if (!do_pre_msg_queue_helper<Event> |
2183 | (evt,::boost::mpl::bool_<is_no_message_queue<library_sm>::type::value>())) |
2184 | { |
2185 | // wait for the end of current processing |
2186 | return HANDLED_TRUE; |
2187 | } |
2188 | else |
2189 | { |
2190 | // Process event |
2191 | HandledEnum handled = this->do_process_helper<Event>( |
2192 | evt, |
2193 | ::boost::mpl::bool_<is_no_exception_thrown<library_sm>::type::value>(), |
2194 | (EVENT_SOURCE_DIRECT & source)); |
2195 | |
2196 | // at this point we allow the next transition be executed without enqueing |
2197 | // so that completion events and deferred events execute now (if any) |
2198 | do_allow_event_processing_after_transition( |
2199 | ::boost::mpl::bool_<is_no_message_queue<library_sm>::type::value>()); |
2200 | |
2201 | // Process completion transitions BEFORE any other event in the |
2202 | // pool (UML Standard 2.3 15.3.14) |
2203 | handle_eventless_transitions_helper<library_sm> |
2204 | eventless_helper(this,(HANDLED_TRUE & handled)); |
2205 | eventless_helper.process_completion_event(source); |
2206 | |
2207 | // After handling, take care of the deferred events, but only if |
2208 | // we're not already processing from the deferred queue. |
2209 | do_handle_prio_msg_queue_deferred_queue( |
2210 | source,handled, |
2211 | ::boost::mpl::bool_<has_event_queue_before_deferred_queue<library_sm>::type::value>()); |
2212 | return handled; |
2213 | } |
2214 | } |
2215 | |
2216 | // minimum event processing without exceptions, queues, etc. |
2217 | template<class Event> |
2218 | HandledEnum do_process_event(Event const& evt, bool is_direct_call) |
2219 | { |
2220 | HandledEnum handled = HANDLED_FALSE; |
2221 | |
2222 | // dispatch the event to every region |
2223 | region_processing_helper<Derived> helper(this,handled); |
2224 | helper.process(evt); |
2225 | |
2226 | // if the event has not been handled and we have orthogonal zones, then |
2227 | // generate an error on every active state |
2228 | // for state machine states contained in other state machines, do not handle |
2229 | // but let the containing sm handle the error, unless the event was generated in this fsm |
2230 | // (by calling process_event on this fsm object, is_direct_call == true) |
2231 | // completion events do not produce an error |
2232 | if ( (!is_contained() || is_direct_call) && !handled && !is_completion_event<Event>::type::value) |
2233 | { |
2234 | for (int i=0; i<nr_regions::value;++i) |
2235 | { |
2236 | this->no_transition(evt,*this,this->m_states[i]); |
2237 | } |
2238 | } |
2239 | return handled; |
2240 | } |
2241 | |
2242 | // default row arguments for the compilers which accept this |
2243 | template <class Event> |
2244 | bool no_guard(Event const&){return true;} |
2245 | template <class Event> |
2246 | void no_action(Event const&){} |
2247 | |
2248 | #ifndef BOOST_NO_RTTI |
2249 | HandledEnum process_any_event( ::boost::any const& evt); |
2250 | #endif |
2251 | |
2252 | private: |
2253 | // composite accept implementation. First calls accept on the composite, then accept on all its active states. |
2254 | void composite_accept() |
2255 | { |
2256 | this->accept(); |
2257 | this->visit_current_states(); |
2258 | } |
2259 | |
2260 | #define MSM_COMPOSITE_ACCEPT_SUB(z, n, unused) ARG ## n vis ## n |
2261 | #define MSM_COMPOSITE_ACCEPT_SUB2(z, n, unused) boost::ref( vis ## n ) |
2262 | #define MSM_COMPOSITE_ACCEPT_EXECUTE(z, n, unused) \ |
2263 | template <BOOST_PP_ENUM_PARAMS(n, class ARG)> \ |
2264 | void composite_accept(BOOST_PP_ENUM(n, MSM_COMPOSITE_ACCEPT_SUB, ~ ) ) \ |
2265 | { \ |
2266 | this->accept(BOOST_PP_ENUM_PARAMS(n,vis)); \ |
2267 | this->visit_current_states(BOOST_PP_ENUM(n,MSM_COMPOSITE_ACCEPT_SUB2, ~)); \ |
2268 | } |
2269 | BOOST_PP_REPEAT_FROM_TO(1,BOOST_PP_ADD(BOOST_MSM_VISITOR_ARG_SIZE,1), MSM_COMPOSITE_ACCEPT_EXECUTE, ~) |
2270 | #undef MSM_COMPOSITE_ACCEPT_EXECUTE |
2271 | #undef MSM_COMPOSITE_ACCEPT_SUB |
2272 | #undef MSM_COMPOSITE_ACCEPT_SUB2 |
2273 | |
2274 | // helper used to call the init states at the start of the state machine |
2275 | template <class Event> |
2276 | struct call_init |
2277 | { |
2278 | call_init(Event const& an_event,library_sm* self_): |
2279 | evt(an_event),self(self_){} |
2280 | template <class State> |
2281 | void operator()(boost::msm::wrap<State> const&) |
2282 | { |
2283 | execute_entry(::boost::fusion::at_key<State>(self->m_substate_list),evt,*self); |
2284 | } |
2285 | private: |
2286 | Event const& evt; |
2287 | library_sm* self; |
2288 | }; |
2289 | // helper for flag handling. Uses OR by default on orthogonal zones. |
2290 | template <class Flag,bool orthogonalStates> |
2291 | struct FlagHelper |
2292 | { |
2293 | static bool helper(library_sm const& sm,flag_handler* ) |
2294 | { |
2295 | // by default we use OR to accumulate the flags |
2296 | return sm.is_flag_active<Flag,Flag_OR>(); |
2297 | } |
2298 | }; |
2299 | template <class Flag> |
2300 | struct FlagHelper<Flag,false> |
2301 | { |
2302 | static bool helper(library_sm const& sm,flag_handler* flags_entries) |
2303 | { |
2304 | // just one active state, so we can call operator[] with 0 |
2305 | return flags_entries[sm.current_state()[0]](sm); |
2306 | } |
2307 | }; |
2308 | // handling of flag |
2309 | // defines a true and false functions plus a forwarding one for composite states |
2310 | template <class StateType,class Flag> |
2311 | struct FlagHandler |
2312 | { |
2313 | static bool flag_true(library_sm const& ) |
2314 | { |
2315 | return true; |
2316 | } |
2317 | static bool flag_false(library_sm const& ) |
2318 | { |
2319 | return false; |
2320 | } |
2321 | static bool forward(library_sm const& fsm) |
2322 | { |
2323 | return ::boost::fusion::at_key<StateType>(fsm.m_substate_list).template is_flag_active<Flag>(); |
2324 | } |
2325 | }; |
2326 | template <class Flag> |
2327 | struct init_flags |
2328 | { |
2329 | private: |
2330 | // helper function, helps hiding the forward function for non-state machines states. |
2331 | template <class T> |
2332 | void helper (flag_handler* an_entry,int offset, ::boost::mpl::true_ const & ) |
2333 | { |
2334 | // composite => forward |
2335 | an_entry[offset] = &FlagHandler<T,Flag>::forward; |
2336 | } |
2337 | template <class T> |
2338 | void helper (flag_handler* an_entry,int offset, ::boost::mpl::false_ const & ) |
2339 | { |
2340 | // default no flag |
2341 | an_entry[offset] = &FlagHandler<T,Flag>::flag_false; |
2342 | } |
2343 | // attributes |
2344 | flag_handler* entries; |
2345 | |
2346 | public: |
2347 | init_flags(flag_handler* entries_) |
2348 | : entries(entries_) |
2349 | {} |
2350 | |
2351 | // Flags initializer function object, used with mpl::for_each |
2352 | template <class StateType> |
2353 | void operator()( ::boost::msm::wrap<StateType> const& ) |
2354 | { |
2355 | typedef typename get_flag_list<StateType>::type flags; |
2356 | typedef typename ::boost::mpl::contains<flags,Flag >::type found; |
2357 | |
2358 | BOOST_STATIC_CONSTANT(int, state_id = (get_state_id<stt,StateType>::type::value)); |
2359 | if (found::type::value) |
2360 | { |
2361 | // the type defined the flag => true |
2362 | entries[state_id] = &FlagHandler<StateType,Flag>::flag_true; |
2363 | } |
2364 | else |
2365 | { |
2366 | // false or forward |
2367 | typedef typename ::boost::mpl::and_< |
2368 | typename is_composite_state<StateType>::type, |
2369 | typename ::boost::mpl::not_< |
2370 | typename has_non_forwarding_flag<Flag>::type>::type >::type composite_no_forward; |
2371 | |
2372 | helper<StateType>(entries,state_id,::boost::mpl::bool_<composite_no_forward::type::value>()); |
2373 | } |
2374 | } |
2375 | }; |
2376 | // maintains for every flag a static array containing the flag value for every state |
2377 | template <class Flag> |
2378 | flag_handler* get_entries_for_flag() const |
2379 | { |
2380 | BOOST_STATIC_CONSTANT(int, max_state = (mpl::size<state_list>::value)); |
2381 | |
2382 | static flag_handler flags_entries[max_state]; |
2383 | // build a state list, but only once |
2384 | static flag_handler* flags_entries_ptr = |
2385 | (::boost::mpl::for_each<state_list, boost::msm::wrap< ::boost::mpl::placeholders::_1> > |
2386 | (init_flags<Flag>(flags_entries)), |
2387 | flags_entries); |
2388 | return flags_entries_ptr; |
2389 | } |
2390 | |
2391 | // helper used to create a state using the correct constructor |
2392 | template <class State, class Enable=void> |
2393 | struct create_state_helper |
2394 | { |
2395 | static void set_sm(library_sm* ) |
2396 | { |
2397 | // state doesn't need its sm |
2398 | } |
2399 | }; |
2400 | // create a state requiring a pointer to the state machine |
2401 | template <class State> |
2402 | struct create_state_helper<State,typename boost::enable_if<typename State::needs_sm >::type> |
2403 | { |
2404 | static void set_sm(library_sm* sm) |
2405 | { |
2406 | // create and set the fsm |
2407 | ::boost::fusion::at_key<State>(sm->m_substate_list).set_sm_ptr(sm); |
2408 | } |
2409 | }; |
2410 | // main unspecialized helper class |
2411 | template <class StateType,int ARGS> |
2412 | struct visitor_args; |
2413 | |
2414 | #define MSM_VISITOR_ARGS_SUB(z, n, unused) BOOST_PP_CAT(::boost::placeholders::_,BOOST_PP_ADD(n,1)) |
2415 | #define MSM_VISITOR_ARGS_TYPEDEF_SUB(z, n, unused) typename StateType::accept_sig::argument ## n |
2416 | |
2417 | #define MSM_VISITOR_ARGS_EXECUTE(z, n, unused) \ |
2418 | template <class StateType> \ |
2419 | struct visitor_args<StateType,n> \ |
2420 | { \ |
2421 | template <class State> \ |
2422 | static typename enable_if_c<!is_composite_state<State>::value,void >::type \ |
2423 | helper (library_sm* sm, \ |
2424 | int id,StateType& astate) \ |
2425 | { \ |
2426 | sm->m_visitors.insert(id, boost::bind(&StateType::accept, \ |
2427 | ::boost::ref(astate) BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM(n, MSM_VISITOR_ARGS_SUB, ~) )); \ |
2428 | } \ |
2429 | template <class State> \ |
2430 | static typename enable_if_c<is_composite_state<State>::value,void >::type \ |
2431 | helper (library_sm* sm, \ |
2432 | int id,StateType& astate) \ |
2433 | { \ |
2434 | void (StateType::*caccept)(BOOST_PP_ENUM(n, MSM_VISITOR_ARGS_TYPEDEF_SUB, ~ ) ) \ |
2435 | = &StateType::composite_accept; \ |
2436 | sm->m_visitors.insert(id, boost::bind(caccept, \ |
2437 | ::boost::ref(astate) BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM(n, MSM_VISITOR_ARGS_SUB, ~) )); \ |
2438 | } \ |
2439 | }; |
2440 | BOOST_PP_REPEAT(BOOST_PP_ADD(BOOST_MSM_VISITOR_ARG_SIZE,1), MSM_VISITOR_ARGS_EXECUTE, ~) |
2441 | #undef MSM_VISITOR_ARGS_EXECUTE |
2442 | #undef MSM_VISITOR_ARGS_SUB |
2443 | |
2444 | // the IBM compiler seems to have problems with nested classes |
2445 | // the same seems to apply to the Apple version of gcc 4.0.1 (just in case we do for < 4.1) |
2446 | // and also to MS VC < 8 |
2447 | #if defined (__IBMCPP__) || (__GNUC__ == 4 && __GNUC_MINOR__ < 1) || (defined(_MSC_VER) && (_MSC_VER < 1400)) |
2448 | public: |
2449 | #endif |
2450 | template<class ContainingSM> |
2451 | void set_containing_sm(ContainingSM* sm) |
2452 | { |
2453 | m_is_included=true; |
2454 | ::boost::fusion::for_each(m_substate_list,add_state<ContainingSM>(this,sm)); |
2455 | } |
2456 | #if defined (__IBMCPP__) || (__GNUC__ == 4 && __GNUC_MINOR__ < 1) || (defined(_MSC_VER) && (_MSC_VER < 1400)) |
2457 | private: |
2458 | #endif |
2459 | // A function object for use with mpl::for_each that stuffs |
2460 | // states into the state list. |
2461 | template<class ContainingSM> |
2462 | struct add_state |
2463 | { |
2464 | add_state(library_sm* self_,ContainingSM* sm) |
2465 | : self(self_),containing_sm(sm){} |
2466 | |
2467 | // State is a sub fsm with exit pseudo states and gets a pointer to this fsm, so it can build a callback |
2468 | template <class StateType> |
2469 | typename ::boost::enable_if< |
2470 | typename is_composite_state<StateType>::type,void >::type |
2471 | new_state_helper(boost::msm::back::dummy<0> = 0) const |
2472 | { |
2473 | ::boost::fusion::at_key<StateType>(self->m_substate_list).set_containing_sm(containing_sm); |
2474 | } |
2475 | // State is a sub fsm without exit pseudo states and does not get a callback to this fsm |
2476 | // or state is a normal state and needs nothing except creation |
2477 | template <class StateType> |
2478 | typename ::boost::enable_if< |
2479 | typename boost::mpl::and_<typename boost::mpl::not_ |
2480 | <typename is_composite_state<StateType>::type>::type, |
2481 | typename boost::mpl::not_ |
2482 | <typename is_pseudo_exit<StateType>::type>::type |
2483 | >::type,void>::type |
2484 | new_state_helper( ::boost::msm::back::dummy<1> = 0) const |
2485 | { |
2486 | //nothing to do |
2487 | } |
2488 | // state is exit pseudo state and gets callback to target fsm |
2489 | template <class StateType> |
2490 | typename ::boost::enable_if<typename is_pseudo_exit<StateType>::type,void >::type |
2491 | new_state_helper( ::boost::msm::back::dummy<2> = 0) const |
2492 | { |
2493 | execute_return (ContainingSM::*pf) (typename StateType::event const& evt)= |
2494 | &ContainingSM::process_event; |
2495 | ::boost::function<execute_return (typename StateType::event const&)> fct = |
2496 | ::boost::bind(pf,containing_sm,::boost::placeholders::_1); |
2497 | ::boost::fusion::at_key<StateType>(self->m_substate_list).set_forward_fct(fct); |
2498 | } |
2499 | // for every defined state in the sm |
2500 | template <class State> |
2501 | void operator()( State const&) const |
2502 | { |
2503 | //create a new state with the defined id and type |
2504 | BOOST_STATIC_CONSTANT(int, state_id = (get_state_id<stt,State>::value)); |
2505 | |
2506 | this->new_state_helper<State>(), |
2507 | create_state_helper<State>::set_sm(self); |
2508 | // create a visitor callback |
2509 | visitor_helper(state_id,::boost::fusion::at_key<State>(self->m_substate_list), |
2510 | ::boost::mpl::bool_<has_accept_sig<State>::type::value>()); |
2511 | } |
2512 | private: |
2513 | // support possible use of a visitor if accept_sig is defined |
2514 | template <class StateType> |
2515 | void visitor_helper(int id,StateType& astate, ::boost::mpl::true_ const & ) const |
2516 | { |
2517 | visitor_args<StateType,StateType::accept_sig::args_number>:: |
2518 | template helper<StateType>(self,id,astate); |
2519 | } |
2520 | template <class StateType> |
2521 | void visitor_helper(int ,StateType& , ::boost::mpl::false_ const &) const |
2522 | { |
2523 | // nothing to do |
2524 | } |
2525 | |
2526 | library_sm* self; |
2527 | ContainingSM* containing_sm; |
2528 | }; |
2529 | |
2530 | // helper used to copy every state if needed |
2531 | struct copy_helper |
2532 | { |
2533 | copy_helper(library_sm* sm): |
2534 | m_sm(sm){} |
2535 | template <class StateType> |
2536 | void operator()( ::boost::msm::wrap<StateType> const& ) |
2537 | { |
2538 | BOOST_STATIC_CONSTANT(int, state_id = (get_state_id<stt,StateType>::type::value)); |
2539 | // possibly also set the visitor |
2540 | visitor_helper<StateType>(state_id); |
2541 | |
2542 | // and for states that keep a pointer to the fsm, reset the pointer |
2543 | create_state_helper<StateType>::set_sm(m_sm); |
2544 | } |
2545 | template <class StateType> |
2546 | typename ::boost::enable_if<typename has_accept_sig<StateType>::type,void >::type |
2547 | visitor_helper(int id) const |
2548 | { |
2549 | visitor_args<StateType,StateType::accept_sig::args_number>::template helper<StateType> |
2550 | (m_sm,id,::boost::fusion::at_key<StateType>(m_sm->m_substate_list)); |
2551 | } |
2552 | template <class StateType> |
2553 | typename ::boost::disable_if<typename has_accept_sig<StateType>::type,void >::type |
2554 | visitor_helper(int) const |
2555 | { |
2556 | // nothing to do |
2557 | } |
2558 | |
2559 | library_sm* m_sm; |
2560 | }; |
2561 | // helper to copy the active states attribute |
2562 | template <class region_id,int Dummy=0> |
2563 | struct region_copy_helper |
2564 | { |
2565 | static void do_copy(library_sm* self_,library_sm const& rhs) |
2566 | { |
2567 | self_->m_states[region_id::value] = rhs.m_states[region_id::value]; |
2568 | region_copy_helper< ::boost::mpl::int_<region_id::value+1> >::do_copy(self_,rhs); |
2569 | } |
2570 | }; |
2571 | template <int Dummy> |
2572 | struct region_copy_helper< ::boost::mpl::int_<nr_regions::value>,Dummy> |
2573 | { |
2574 | // end of processing |
2575 | static void do_copy(library_sm*,library_sm const& ){} |
2576 | }; |
2577 | // copy functions for deep copy (no need of a 2nd version for NoCopy as noncopyable handles it) |
2578 | void do_copy (library_sm const& rhs, |
2579 | ::boost::msm::back::dummy<0> = 0) |
2580 | { |
2581 | // deep copy simply assigns the data |
2582 | region_copy_helper< ::boost::mpl::int_<0> >::do_copy(this,rhs); |
2583 | m_events_queue = rhs.m_events_queue; |
2584 | m_deferred_events_queue = rhs.m_deferred_events_queue; |
2585 | m_history = rhs.m_history; |
2586 | m_event_processing = rhs.m_event_processing; |
2587 | m_is_included = rhs.m_is_included; |
2588 | m_substate_list = rhs.m_substate_list; |
2589 | // except for the states themselves, which get duplicated |
2590 | |
2591 | ::boost::mpl::for_each<state_list, ::boost::msm::wrap< ::boost::mpl::placeholders::_1> > |
2592 | (copy_helper(this)); |
2593 | } |
2594 | |
2595 | // helper used to call the correct entry/exit method |
2596 | // unfortunately in O(number of states in the sub-sm) but should be better than a virtual call |
2597 | template<class Event,bool is_entry> |
2598 | struct entry_exit_helper |
2599 | { |
2600 | entry_exit_helper(int id,Event const& e,library_sm* self_): |
2601 | state_id(id),evt(e),self(self_){} |
2602 | // helper for entry actions |
2603 | template <class IsEntry,class State> |
2604 | typename ::boost::enable_if<typename IsEntry::type,void >::type |
2605 | helper( ::boost::msm::back::dummy<0> = 0) |
2606 | { |
2607 | BOOST_STATIC_CONSTANT(int, id = (get_state_id<stt,State>::value)); |
2608 | if (id == state_id) |
2609 | { |
2610 | execute_entry<State>(::boost::fusion::at_key<State>(self->m_substate_list),evt,*self); |
2611 | } |
2612 | } |
2613 | // helper for exit actions |
2614 | template <class IsEntry,class State> |
2615 | typename boost::disable_if<typename IsEntry::type,void >::type |
2616 | helper( ::boost::msm::back::dummy<1> = 0) |
2617 | { |
2618 | BOOST_STATIC_CONSTANT(int, id = (get_state_id<stt,State>::value)); |
2619 | if (id == state_id) |
2620 | { |
2621 | execute_exit<State>(::boost::fusion::at_key<State>(self->m_substate_list),evt,*self); |
2622 | } |
2623 | } |
2624 | // iterates through all states to find the one to be activated |
2625 | template <class State> |
2626 | void operator()( ::boost::msm::wrap<State> const&) |
2627 | { |
2628 | entry_exit_helper<Event,is_entry>::template helper< ::boost::mpl::bool_<is_entry>,State >(); |
2629 | } |
2630 | private: |
2631 | int state_id; |
2632 | Event const& evt; |
2633 | library_sm* self; |
2634 | }; |
2635 | |
2636 | // helper to start the fsm |
2637 | template <class region_id,int Dummy=0> |
2638 | struct region_start_helper |
2639 | { |
2640 | template<class Event> |
2641 | static void do_start(library_sm* self_,Event const& incomingEvent) |
2642 | { |
2643 | //forward the event for handling by sub state machines |
2644 | ::boost::mpl::for_each<state_list, ::boost::msm::wrap< ::boost::mpl::placeholders::_1> > |
2645 | (entry_exit_helper<Event,true>(self_->m_states[region_id::value],incomingEvent,self_)); |
2646 | region_start_helper |
2647 | < ::boost::mpl::int_<region_id::value+1> >::do_start(self_,incomingEvent); |
2648 | } |
2649 | }; |
2650 | template <int Dummy> |
2651 | struct region_start_helper< ::boost::mpl::int_<nr_regions::value>,Dummy> |
2652 | { |
2653 | // end of processing |
2654 | template<class Event> |
2655 | static void do_start(library_sm*,Event const& ){} |
2656 | }; |
2657 | // start for states machines which are themselves embedded in other state machines (composites) |
2658 | template <class Event> |
2659 | void internal_start(Event const& incomingEvent) |
2660 | { |
2661 | region_start_helper< ::boost::mpl::int_<0> >::do_start(this,incomingEvent); |
2662 | // give a chance to handle an anonymous (eventless) transition |
2663 | handle_eventless_transitions_helper<library_sm> eventless_helper(this,true); |
2664 | eventless_helper.process_completion_event(); |
2665 | } |
2666 | |
2667 | template <class StateType> |
2668 | struct find_region_id |
2669 | { |
2670 | template <int region,int Dummy=0> |
2671 | struct In |
2672 | { |
2673 | enum {region_index=region}; |
2674 | }; |
2675 | // if the user provides no region, find it! |
2676 | template<int Dummy> |
2677 | struct In<-1,Dummy> |
2678 | { |
2679 | typedef typename build_orthogonal_regions< |
2680 | library_sm, |
2681 | initial_states |
2682 | >::type all_regions; |
2683 | enum {region_index= find_region_index<all_regions,StateType>::value }; |
2684 | }; |
2685 | enum {region_index = In<StateType::zone_index>::region_index }; |
2686 | }; |
2687 | // helper used to set the correct state as active state upon entry into a fsm |
2688 | struct direct_event_start_helper |
2689 | { |
2690 | direct_event_start_helper(library_sm* self_):self(self_){} |
2691 | // this variant is for the standard case, entry due to activation of the containing FSM |
2692 | template <class EventType,class FsmType> |
2693 | typename ::boost::disable_if<typename has_direct_entry<EventType>::type,void>::type |
2694 | operator()(EventType const& evt,FsmType& fsm, ::boost::msm::back::dummy<0> = 0) |
2695 | { |
2696 | (static_cast<Derived*>(self))->on_entry(evt,fsm); |
2697 | self->internal_start(evt); |
2698 | } |
2699 | |
2700 | // this variant is for the direct entry case (just one entry, not a sequence of entries) |
2701 | template <class EventType,class FsmType> |
2702 | typename ::boost::enable_if< |
2703 | typename ::boost::mpl::and_< |
2704 | typename ::boost::mpl::not_< typename is_pseudo_entry< |
2705 | typename EventType::active_state>::type >::type, |
2706 | typename ::boost::mpl::and_<typename has_direct_entry<EventType>::type, |
2707 | typename ::boost::mpl::not_<typename ::boost::mpl::is_sequence |
2708 | <typename EventType::active_state>::type >::type |
2709 | >::type>::type,void |
2710 | >::type |
2711 | operator()(EventType const& evt,FsmType& fsm, ::boost::msm::back::dummy<1> = 0) |
2712 | { |
2713 | (static_cast<Derived*>(self))->on_entry(evt,fsm); |
2714 | int state_id = get_state_id<stt,typename EventType::active_state::wrapped_entry>::value; |
2715 | BOOST_STATIC_ASSERT(find_region_id<typename EventType::active_state::wrapped_entry>::region_index >= 0); |
2716 | BOOST_STATIC_ASSERT(find_region_id<typename EventType::active_state::wrapped_entry>::region_index < nr_regions::value); |
2717 | // just set the correct zone, the others will be default/history initialized |
2718 | self->m_states[find_region_id<typename EventType::active_state::wrapped_entry>::region_index] = state_id; |
2719 | self->internal_start(evt.m_event); |
2720 | } |
2721 | |
2722 | // this variant is for the fork entry case (a sequence on entries) |
2723 | template <class EventType,class FsmType> |
2724 | typename ::boost::enable_if< |
2725 | typename ::boost::mpl::and_< |
2726 | typename ::boost::mpl::not_< |
2727 | typename is_pseudo_entry<typename EventType::active_state>::type >::type, |
2728 | typename ::boost::mpl::and_<typename has_direct_entry<EventType>::type, |
2729 | typename ::boost::mpl::is_sequence< |
2730 | typename EventType::active_state>::type |
2731 | >::type>::type,void |
2732 | >::type |
2733 | operator()(EventType const& evt,FsmType& fsm, ::boost::msm::back::dummy<2> = 0) |
2734 | { |
2735 | (static_cast<Derived*>(self))->on_entry(evt,fsm); |
2736 | ::boost::mpl::for_each<typename EventType::active_state, |
2737 | ::boost::msm::wrap< ::boost::mpl::placeholders::_1> > |
2738 | (fork_helper<EventType>(self,evt)); |
2739 | // set the correct zones, the others (if any) will be default/history initialized |
2740 | self->internal_start(evt.m_event); |
2741 | } |
2742 | |
2743 | // this variant is for the pseudo state entry case |
2744 | template <class EventType,class FsmType> |
2745 | typename ::boost::enable_if< |
2746 | typename is_pseudo_entry<typename EventType::active_state >::type,void |
2747 | >::type |
2748 | operator()(EventType const& evt,FsmType& fsm, ::boost::msm::back::dummy<3> = 0) |
2749 | { |
2750 | // entry on the FSM |
2751 | (static_cast<Derived*>(self))->on_entry(evt,fsm); |
2752 | int state_id = get_state_id<stt,typename EventType::active_state::wrapped_entry>::value; |
2753 | BOOST_STATIC_ASSERT(find_region_id<typename EventType::active_state::wrapped_entry>::region_index >= 0); |
2754 | BOOST_STATIC_ASSERT(find_region_id<typename EventType::active_state::wrapped_entry>::region_index < nr_regions::value); |
2755 | // given region starts with the entry pseudo state as active state |
2756 | self->m_states[find_region_id<typename EventType::active_state::wrapped_entry>::region_index] = state_id; |
2757 | self->internal_start(evt.m_event); |
2758 | // and we process the transition in the zone of the newly active state |
2759 | // (entry pseudo states are, according to UML, a state connecting 1 transition outside to 1 inside |
2760 | self->process_event(evt.m_event); |
2761 | } |
2762 | private: |
2763 | // helper for the fork case, does almost like the direct entry |
2764 | library_sm* self; |
2765 | template <class EventType> |
2766 | struct fork_helper |
2767 | { |
2768 | fork_helper(library_sm* self_,EventType const& evt_): |
2769 | helper_self(self_),helper_evt(evt_){} |
2770 | template <class StateType> |
2771 | void operator()( ::boost::msm::wrap<StateType> const& ) |
2772 | { |
2773 | int state_id = get_state_id<stt,typename StateType::wrapped_entry>::value; |
2774 | BOOST_STATIC_ASSERT(find_region_id<typename StateType::wrapped_entry>::region_index >= 0); |
2775 | BOOST_STATIC_ASSERT(find_region_id<typename StateType::wrapped_entry>::region_index < nr_regions::value); |
2776 | helper_self->m_states[find_region_id<typename StateType::wrapped_entry>::region_index] = state_id; |
2777 | } |
2778 | private: |
2779 | library_sm* helper_self; |
2780 | EventType const& helper_evt; |
2781 | }; |
2782 | }; |
2783 | |
2784 | // helper for entry |
2785 | template <class region_id,int Dummy=0> |
2786 | struct region_entry_exit_helper |
2787 | { |
2788 | template<class Event> |
2789 | static void do_entry(library_sm* self_,Event const& incomingEvent) |
2790 | { |
2791 | self_->m_states[region_id::value] = |
2792 | self_->m_history.history_entry(incomingEvent)[region_id::value]; |
2793 | region_entry_exit_helper |
2794 | < ::boost::mpl::int_<region_id::value+1> >::do_entry(self_,incomingEvent); |
2795 | } |
2796 | template<class Event> |
2797 | static void do_exit(library_sm* self_,Event const& incomingEvent) |
2798 | { |
2799 | ::boost::mpl::for_each<state_list, ::boost::msm::wrap< ::boost::mpl::placeholders::_1> > |
2800 | (entry_exit_helper<Event,false>(self_->m_states[region_id::value],incomingEvent,self_)); |
2801 | region_entry_exit_helper |
2802 | < ::boost::mpl::int_<region_id::value+1> >::do_exit(self_,incomingEvent); |
2803 | } |
2804 | }; |
2805 | template <int Dummy> |
2806 | struct region_entry_exit_helper< ::boost::mpl::int_<nr_regions::value>,Dummy> |
2807 | { |
2808 | // end of processing |
2809 | template<class Event> |
2810 | static void do_entry(library_sm*,Event const& ){} |
2811 | template<class Event> |
2812 | static void do_exit(library_sm*,Event const& ){} |
2813 | }; |
2814 | // entry/exit for states machines which are themselves embedded in other state machines (composites) |
2815 | template <class Event,class FsmType> |
2816 | void do_entry(Event const& incomingEvent,FsmType& fsm) |
2817 | { |
2818 | // by default we activate the history/init states, can be overwritten by direct_event_start_helper |
2819 | region_entry_exit_helper< ::boost::mpl::int_<0> >::do_entry(this,incomingEvent); |
2820 | // block immediate handling of events |
2821 | m_event_processing = true; |
2822 | // if the event is generating a direct entry/fork, set the current state(s) to the direct state(s) |
2823 | direct_event_start_helper(this)(incomingEvent,fsm); |
2824 | // handle messages which were generated and blocked in the init calls |
2825 | m_event_processing = false; |
2826 | // look for deferred events waiting |
2827 | handle_defer_helper<library_sm> defer_helper(m_deferred_events_queue); |
2828 | defer_helper.do_handle_deferred(true); |
2829 | process_message_queue(this); |
2830 | } |
2831 | template <class Event,class FsmType> |
2832 | void do_exit(Event const& incomingEvent,FsmType& fsm) |
2833 | { |
2834 | // first recursively exit the sub machines |
2835 | // forward the event for handling by sub state machines |
2836 | region_entry_exit_helper< ::boost::mpl::int_<0> >::do_exit(this,incomingEvent); |
2837 | // then call our own exit |
2838 | (static_cast<Derived*>(this))->on_exit(incomingEvent,fsm); |
2839 | // give the history a chance to handle this (or not). |
2840 | m_history.history_exit(this->m_states); |
2841 | // history decides what happens with deferred events |
2842 | if (!m_history.process_deferred_events(incomingEvent)) |
2843 | { |
2844 | clear_deferred_queue(); |
2845 | } |
2846 | } |
2847 | |
2848 | // the IBM and VC<8 compilers seem to have problems with the friend declaration of dispatch_table |
2849 | #if defined (__IBMCPP__) || (defined(_MSC_VER) && (_MSC_VER < 1400)) |
2850 | public: |
2851 | #endif |
2852 | // no transition for event. |
2853 | template <class Event> |
2854 | static HandledEnum call_no_transition(library_sm& , int , int , Event const& ) |
2855 | { |
2856 | return HANDLED_FALSE; |
2857 | } |
2858 | // no transition for event for internal transitions (not an error). |
2859 | template <class Event> |
2860 | static HandledEnum call_no_transition_internal(library_sm& , int , int , Event const& ) |
2861 | { |
2862 | //// reject to give others a chance to handle |
2863 | //return HANDLED_GUARD_REJECT; |
2864 | return HANDLED_FALSE; |
2865 | } |
2866 | // called for deferred events. Address set in the dispatch_table at init |
2867 | template <class Event> |
2868 | static HandledEnum defer_transition(library_sm& fsm, int , int , Event const& e) |
2869 | { |
2870 | fsm.defer_event(e); |
2871 | return HANDLED_DEFERRED; |
2872 | } |
2873 | // called for completion events. Default address set in the dispatch_table at init |
2874 | // prevents no-transition detection for completion events |
2875 | template <class Event> |
2876 | static HandledEnum default_eventless_transition(library_sm&, int, int , Event const&) |
2877 | { |
2878 | return HANDLED_FALSE; |
2879 | } |
2880 | #if defined (__IBMCPP__) || (defined(_MSC_VER) && (_MSC_VER < 1400)) |
2881 | private: |
2882 | #endif |
2883 | // removes one event from the message queue and processes it |
2884 | template <class StateType> |
2885 | void process_message_queue(StateType*, |
2886 | typename ::boost::disable_if<typename is_no_message_queue<StateType>::type,void >::type* = 0) |
2887 | { |
2888 | // Iteratively process all events from the message queue. |
2889 | while (!m_events_queue.m_events_queue.empty()) |
2890 | { |
2891 | transition_fct next = m_events_queue.m_events_queue.front(); |
2892 | m_events_queue.m_events_queue.pop_front(); |
2893 | next(); |
2894 | } |
2895 | } |
2896 | template <class StateType> |
2897 | void process_message_queue(StateType*, |
2898 | typename ::boost::enable_if<typename is_no_message_queue<StateType>::type,void >::type* = 0) |
2899 | { |
2900 | // nothing to process |
2901 | } |
2902 | // helper function. In cases where the event is wrapped (target is a direct entry states) |
2903 | // we want to send only the real event to on_entry, not the wrapper. |
2904 | template <class EventType> |
2905 | static |
2906 | typename boost::enable_if<typename has_direct_entry<EventType>::type,typename EventType::contained_event const& >::type |
2907 | remove_direct_entry_event_wrapper(EventType const& evt,boost::msm::back::dummy<0> = 0) |
2908 | { |
2909 | return evt.m_event; |
2910 | } |
2911 | template <class EventType> |
2912 | static typename boost::disable_if<typename has_direct_entry<EventType>::type,EventType const& >::type |
2913 | remove_direct_entry_event_wrapper(EventType const& evt,boost::msm::back::dummy<1> = 0) |
2914 | { |
2915 | // identity. No wrapper |
2916 | return evt; |
2917 | } |
2918 | // calls the entry/exit or on_entry/on_exit depending on the state type |
2919 | // (avoids calling virtually) |
2920 | // variant for FSMs |
2921 | template <class StateType,class EventType,class FsmType> |
2922 | static |
2923 | typename boost::enable_if<typename is_composite_state<StateType>::type,void >::type |
2924 | execute_entry(StateType& astate,EventType const& evt,FsmType& fsm,boost::msm::back::dummy<0> = 0) |
2925 | { |
2926 | // calls on_entry on the fsm then handles direct entries, fork, entry pseudo state |
2927 | astate.do_entry(evt,fsm); |
2928 | } |
2929 | // variant for states |
2930 | template <class StateType,class EventType,class FsmType> |
2931 | static |
2932 | typename ::boost::disable_if< |
2933 | typename ::boost::mpl::or_<typename is_composite_state<StateType>::type, |
2934 | typename is_pseudo_exit<StateType>::type >::type,void >::type |
2935 | execute_entry(StateType& astate,EventType const& evt,FsmType& fsm, ::boost::msm::back::dummy<1> = 0) |
2936 | { |
2937 | // simple call to on_entry |
2938 | astate.on_entry(remove_direct_entry_event_wrapper(evt),fsm); |
2939 | } |
2940 | // variant for exit pseudo states |
2941 | template <class StateType,class EventType,class FsmType> |
2942 | static |
2943 | typename ::boost::enable_if<typename is_pseudo_exit<StateType>::type,void >::type |
2944 | execute_entry(StateType& astate,EventType const& evt,FsmType& fsm, ::boost::msm::back::dummy<2> = 0) |
2945 | { |
2946 | // calls on_entry on the state then forward the event to the transition which should be defined inside the |
2947 | // contained fsm |
2948 | astate.on_entry(evt,fsm); |
2949 | astate.forward_event(evt); |
2950 | } |
2951 | template <class StateType,class EventType,class FsmType> |
2952 | static |
2953 | typename ::boost::enable_if<typename is_composite_state<StateType>::type,void >::type |
2954 | execute_exit(StateType& astate,EventType const& evt,FsmType& fsm, ::boost::msm::back::dummy<0> = 0) |
2955 | { |
2956 | astate.do_exit(evt,fsm); |
2957 | } |
2958 | template <class StateType,class EventType,class FsmType> |
2959 | static |
2960 | typename ::boost::disable_if<typename is_composite_state<StateType>::type,void >::type |
2961 | execute_exit(StateType& astate,EventType const& evt,FsmType& fsm, ::boost::msm::back::dummy<1> = 0) |
2962 | { |
2963 | // simple call to on_exit |
2964 | astate.on_exit(evt,fsm); |
2965 | } |
2966 | |
2967 | // helper allowing special handling of direct entries / fork |
2968 | template <class StateType,class TargetType,class EventType,class FsmType> |
2969 | static |
2970 | typename ::boost::disable_if< |
2971 | typename ::boost::mpl::or_<typename has_explicit_entry_state<TargetType>::type, |
2972 | ::boost::mpl::is_sequence<TargetType> >::type,void>::type |
2973 | convert_event_and_execute_entry(StateType& astate,EventType const& evt, FsmType& fsm, ::boost::msm::back::dummy<1> = 0) |
2974 | { |
2975 | // if the target is a normal state, do the standard entry handling |
2976 | execute_entry<StateType>(astate,evt,fsm); |
2977 | } |
2978 | template <class StateType,class TargetType,class EventType,class FsmType> |
2979 | static |
2980 | typename ::boost::enable_if< |
2981 | typename ::boost::mpl::or_<typename has_explicit_entry_state<TargetType>::type, |
2982 | ::boost::mpl::is_sequence<TargetType> >::type,void >::type |
2983 | convert_event_and_execute_entry(StateType& astate,EventType const& evt, FsmType& fsm, ::boost::msm::back::dummy<0> = 0) |
2984 | { |
2985 | // for the direct entry, pack the event in a wrapper so that we handle it differently during fsm entry |
2986 | execute_entry(astate,msm::back::direct_entry_event<TargetType,EventType>(evt),fsm); |
2987 | } |
2988 | |
2989 | // creates all the states |
2990 | template <class ContainingSM> |
2991 | void fill_states(ContainingSM* containing_sm=0) |
2992 | { |
2993 | // checks that regions are truly orthogonal |
2994 | FsmCheckPolicy::template check_orthogonality<library_sm>(); |
2995 | // checks that all states are reachable |
2996 | FsmCheckPolicy::template check_unreachable_states<library_sm>(); |
2997 | |
2998 | BOOST_STATIC_CONSTANT(int, max_state = (mpl::size<state_list>::value)); |
2999 | // allocate the place without reallocation |
3000 | m_visitors.fill_visitors(max_state); |
3001 | ::boost::fusion::for_each(m_substate_list,add_state<ContainingSM>(this,containing_sm)); |
3002 | |
3003 | } |
3004 | |
3005 | private: |
3006 | template <class StateType,class Enable=void> |
3007 | struct msg_queue_helper |
3008 | { |
3009 | public: |
3010 | msg_queue_helper():m_events_queue(){} |
3011 | events_queue_t m_events_queue; |
3012 | }; |
3013 | template <class StateType> |
3014 | struct msg_queue_helper<StateType, |
3015 | typename ::boost::enable_if<typename is_no_message_queue<StateType>::type >::type> |
3016 | { |
3017 | }; |
3018 | |
3019 | template <class Fsm,class Stt, class Event, class Compile> |
3020 | friend struct dispatch_table; |
3021 | |
3022 | // data members |
3023 | int m_states[nr_regions::value]; |
3024 | msg_queue_helper<library_sm> m_events_queue; |
3025 | deferred_msg_queue_helper |
3026 | <library_sm> m_deferred_events_queue; |
3027 | concrete_history m_history; |
3028 | bool m_event_processing; |
3029 | bool m_is_included; |
3030 | visitor_fct_helper<BaseState> m_visitors; |
3031 | substate_list m_substate_list; |
3032 | |
3033 | |
3034 | }; |
3035 | |
3036 | } } }// boost::msm::back |
3037 | #endif //BOOST_MSM_BACK_STATEMACHINE_H |
3038 | |