| 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 | |