]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | #ifndef BOOST_STATECHART_STATE_MACHINE_HPP_INCLUDED |
2 | #define BOOST_STATECHART_STATE_MACHINE_HPP_INCLUDED | |
3 | ////////////////////////////////////////////////////////////////////////////// | |
4 | // Copyright 2002-2010 Andreas Huber Doenni | |
5 | // Distributed under the Boost Software License, Version 1.0. (See accompany- | |
6 | // ing file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | |
7 | ////////////////////////////////////////////////////////////////////////////// | |
8 | ||
9 | ||
10 | ||
11 | #include <boost/statechart/event.hpp> | |
12 | #include <boost/statechart/null_exception_translator.hpp> | |
13 | #include <boost/statechart/result.hpp> | |
14 | ||
15 | #include <boost/statechart/detail/rtti_policy.hpp> | |
16 | #include <boost/statechart/detail/state_base.hpp> | |
17 | #include <boost/statechart/detail/leaf_state.hpp> | |
18 | #include <boost/statechart/detail/node_state.hpp> | |
19 | #include <boost/statechart/detail/constructor.hpp> | |
20 | #include <boost/statechart/detail/avoid_unused_warning.hpp> | |
21 | ||
22 | #include <boost/mpl/list.hpp> | |
23 | #include <boost/mpl/clear.hpp> | |
24 | #include <boost/mpl/if.hpp> | |
25 | #include <boost/mpl/at.hpp> | |
26 | #include <boost/mpl/integral_c.hpp> | |
27 | #include <boost/mpl/minus.hpp> | |
28 | #include <boost/mpl/equal_to.hpp> | |
29 | ||
30 | #include <boost/intrusive_ptr.hpp> | |
31 | #include <boost/type_traits/is_pointer.hpp> | |
32 | #include <boost/type_traits/remove_reference.hpp> | |
33 | #include <boost/noncopyable.hpp> | |
34 | #include <boost/assert.hpp> | |
35 | #include <boost/static_assert.hpp> | |
36 | #include <boost/polymorphic_cast.hpp> // boost::polymorphic_downcast | |
37 | // BOOST_NO_EXCEPTIONS, BOOST_MSVC, BOOST_MSVC_STD_ITERATOR | |
38 | #include <boost/config.hpp> | |
39 | ||
40 | #include <boost/detail/allocator_utilities.hpp> | |
41 | ||
42 | #ifdef BOOST_MSVC | |
43 | # pragma warning( push ) | |
44 | # pragma warning( disable: 4702 ) // unreachable code (in release mode only) | |
45 | #endif | |
46 | ||
47 | #include <map> | |
48 | ||
49 | #ifdef BOOST_MSVC | |
50 | # pragma warning( pop ) | |
51 | #endif | |
52 | ||
53 | #include <memory> // std::allocator | |
54 | #include <typeinfo> // std::bad_cast | |
55 | #include <functional> // std::less | |
56 | #include <iterator> | |
57 | ||
58 | ||
59 | ||
60 | namespace boost | |
61 | { | |
62 | namespace statechart | |
63 | { | |
64 | namespace detail | |
65 | { | |
66 | ||
67 | ||
68 | ||
69 | ////////////////////////////////////////////////////////////////////////////// | |
70 | template< class StateBaseType, class EventBaseType, class IdType > | |
71 | class send_function | |
72 | { | |
73 | public: | |
74 | ////////////////////////////////////////////////////////////////////////// | |
75 | send_function( | |
76 | StateBaseType & toState, | |
77 | const EventBaseType & evt, | |
78 | IdType eventType | |
79 | ) : | |
80 | toState_( toState ), evt_( evt ), eventType_( eventType ) | |
81 | { | |
82 | } | |
83 | ||
84 | result operator()() | |
85 | { | |
86 | return detail::result_utility::make_result( | |
87 | toState_.react_impl( evt_, eventType_ ) ); | |
88 | } | |
89 | ||
90 | private: | |
91 | ////////////////////////////////////////////////////////////////////////// | |
92 | // avoids C4512 (assignment operator could not be generated) | |
93 | send_function & operator=( const send_function & ); | |
94 | ||
95 | StateBaseType & toState_; | |
96 | const EventBaseType & evt_; | |
97 | IdType eventType_; | |
98 | }; | |
99 | ||
100 | ||
101 | ////////////////////////////////////////////////////////////////////////////// | |
102 | struct state_cast_impl_pointer_target | |
103 | { | |
104 | public: | |
105 | ////////////////////////////////////////////////////////////////////////// | |
106 | template< class StateBaseType > | |
107 | static const StateBaseType * deref_if_necessary( | |
108 | const StateBaseType * pState ) | |
109 | { | |
110 | return pState; | |
111 | } | |
112 | ||
113 | template< class Target, class IdType > | |
114 | static IdType type_id() | |
115 | { | |
116 | Target p = 0; | |
117 | return type_id_impl< IdType >( p ); | |
118 | } | |
119 | ||
120 | static bool found( const void * pFound ) | |
121 | { | |
122 | return pFound != 0; | |
123 | } | |
124 | ||
125 | template< class Target > | |
126 | static Target not_found() | |
127 | { | |
128 | return 0; | |
129 | } | |
130 | ||
131 | private: | |
132 | ////////////////////////////////////////////////////////////////////////// | |
133 | template< class IdType, class Type > | |
134 | static IdType type_id_impl( const Type * ) | |
135 | { | |
136 | return Type::static_type(); | |
137 | } | |
138 | }; | |
139 | ||
140 | struct state_cast_impl_reference_target | |
141 | { | |
142 | template< class StateBaseType > | |
143 | static const StateBaseType & deref_if_necessary( | |
144 | const StateBaseType * pState ) | |
145 | { | |
146 | return *pState; | |
147 | } | |
148 | ||
149 | template< class Target, class IdType > | |
150 | static IdType type_id() | |
151 | { | |
152 | return remove_reference< Target >::type::static_type(); | |
153 | } | |
154 | ||
155 | template< class Dummy > | |
156 | static bool found( const Dummy & ) | |
157 | { | |
158 | return true; | |
159 | } | |
160 | ||
161 | template< class Target > | |
162 | static Target not_found() | |
163 | { | |
164 | throw std::bad_cast(); | |
165 | } | |
166 | }; | |
167 | ||
168 | template< class Target > | |
169 | struct state_cast_impl : public mpl::if_< | |
170 | is_pointer< Target >, | |
171 | state_cast_impl_pointer_target, | |
172 | state_cast_impl_reference_target | |
173 | >::type {}; | |
174 | ||
175 | ||
176 | ////////////////////////////////////////////////////////////////////////////// | |
177 | template< class RttiPolicy > | |
178 | class history_key | |
179 | { | |
180 | public: | |
181 | ////////////////////////////////////////////////////////////////////////// | |
182 | template< class HistorizedState > | |
183 | static history_key make_history_key() | |
184 | { | |
185 | return history_key( | |
186 | HistorizedState::context_type::static_type(), | |
187 | HistorizedState::orthogonal_position::value ); | |
188 | } | |
189 | ||
190 | typename RttiPolicy::id_type history_context_type() const | |
191 | { | |
192 | return historyContextType_; | |
193 | } | |
194 | ||
195 | friend bool operator<( | |
196 | const history_key & left, const history_key & right ) | |
197 | { | |
198 | return | |
199 | std::less< typename RttiPolicy::id_type >()( | |
200 | left.historyContextType_, right.historyContextType_ ) || | |
201 | ( ( left.historyContextType_ == right.historyContextType_ ) && | |
202 | ( left.historizedOrthogonalRegion_ < | |
203 | right.historizedOrthogonalRegion_ ) ); | |
204 | } | |
205 | ||
206 | private: | |
207 | ////////////////////////////////////////////////////////////////////////// | |
208 | history_key( | |
209 | typename RttiPolicy::id_type historyContextType, | |
210 | orthogonal_position_type historizedOrthogonalRegion | |
211 | ) : | |
212 | historyContextType_( historyContextType ), | |
213 | historizedOrthogonalRegion_( historizedOrthogonalRegion ) | |
214 | { | |
215 | } | |
216 | ||
217 | // avoids C4512 (assignment operator could not be generated) | |
218 | history_key & operator=( const history_key & ); | |
219 | ||
220 | const typename RttiPolicy::id_type historyContextType_; | |
221 | const orthogonal_position_type historizedOrthogonalRegion_; | |
222 | }; | |
223 | ||
224 | ||
225 | ||
226 | } // namespace detail | |
227 | ||
228 | ||
229 | ||
230 | ////////////////////////////////////////////////////////////////////////////// | |
231 | template< class MostDerived, | |
232 | class InitialState, | |
92f5a8d4 | 233 | class Allocator = std::allocator< none >, |
7c673cae FG |
234 | class ExceptionTranslator = null_exception_translator > |
235 | class state_machine : noncopyable | |
236 | { | |
237 | public: | |
238 | ////////////////////////////////////////////////////////////////////////// | |
239 | typedef Allocator allocator_type; | |
240 | typedef detail::rtti_policy rtti_policy_type; | |
241 | typedef event_base event_base_type; | |
242 | typedef intrusive_ptr< const event_base_type > event_base_ptr_type; | |
243 | ||
244 | void initiate() | |
245 | { | |
246 | terminate(); | |
247 | ||
248 | { | |
249 | terminator guard( *this, 0 ); | |
250 | detail::result_utility::get_result( translator_( | |
251 | initial_construct_function( *this ), | |
252 | exception_event_handler( *this ) ) ); | |
253 | guard.dismiss(); | |
254 | } | |
255 | ||
256 | process_queued_events(); | |
257 | } | |
258 | ||
259 | void terminate() | |
260 | { | |
261 | terminator guard( *this, 0 ); | |
262 | detail::result_utility::get_result( translator_( | |
263 | terminate_function( *this ), | |
264 | exception_event_handler( *this ) ) ); | |
265 | guard.dismiss(); | |
266 | } | |
267 | ||
268 | bool terminated() const | |
269 | { | |
270 | return pOutermostState_ == 0; | |
271 | } | |
272 | ||
273 | void process_event( const event_base_type & evt ) | |
274 | { | |
275 | if ( send_event( evt ) == detail::do_defer_event ) | |
276 | { | |
277 | deferredEventQueue_.push_back( evt.intrusive_from_this() ); | |
278 | } | |
279 | ||
280 | process_queued_events(); | |
281 | } | |
282 | ||
283 | template< class Target > | |
284 | Target state_cast() const | |
285 | { | |
286 | typedef detail::state_cast_impl< Target > impl; | |
287 | ||
288 | for ( typename state_list_type::const_iterator pCurrentLeafState = | |
289 | currentStates_.begin(); | |
290 | pCurrentLeafState != currentStatesEnd_; | |
291 | ++pCurrentLeafState ) | |
292 | { | |
293 | const state_base_type * pCurrentState( | |
294 | get_pointer( *pCurrentLeafState ) ); | |
295 | ||
296 | while ( pCurrentState != 0 ) | |
297 | { | |
298 | // The unnecessary try/catch overhead for pointer targets is | |
299 | // typically small compared to the cycles dynamic_cast needs | |
300 | #ifndef BOOST_NO_EXCEPTIONS | |
301 | try | |
302 | #endif | |
303 | { | |
304 | Target result = dynamic_cast< Target >( | |
305 | impl::deref_if_necessary( pCurrentState ) ); | |
306 | ||
307 | if ( impl::found( result ) ) | |
308 | { | |
309 | return result; | |
310 | } | |
311 | } | |
312 | #ifndef BOOST_NO_EXCEPTIONS | |
313 | // Intentionally swallow std::bad_cast exceptions. We'll throw one | |
314 | // ourselves when we fail to find a state that can be cast to Target | |
315 | catch ( const std::bad_cast & ) {} | |
316 | #endif | |
317 | ||
318 | pCurrentState = pCurrentState->outer_state_ptr(); | |
319 | } | |
320 | } | |
321 | ||
322 | return impl::template not_found< Target >(); | |
323 | } | |
324 | ||
325 | template< class Target > | |
326 | Target state_downcast() const | |
327 | { | |
328 | typedef detail::state_cast_impl< Target > impl; | |
329 | ||
330 | typename rtti_policy_type::id_type targetType = | |
331 | impl::template type_id< Target, rtti_policy_type::id_type >(); | |
332 | ||
333 | for ( typename state_list_type::const_iterator pCurrentLeafState = | |
334 | currentStates_.begin(); | |
335 | pCurrentLeafState != currentStatesEnd_; | |
336 | ++pCurrentLeafState ) | |
337 | { | |
338 | const state_base_type * pCurrentState( | |
339 | get_pointer( *pCurrentLeafState ) ); | |
340 | ||
341 | while ( pCurrentState != 0 ) | |
342 | { | |
343 | if ( pCurrentState->dynamic_type() == targetType ) | |
344 | { | |
345 | return static_cast< Target >( | |
346 | impl::deref_if_necessary( pCurrentState ) ); | |
347 | } | |
348 | ||
349 | pCurrentState = pCurrentState->outer_state_ptr(); | |
350 | } | |
351 | } | |
352 | ||
353 | return impl::template not_found< Target >(); | |
354 | } | |
355 | ||
356 | typedef detail::state_base< allocator_type, rtti_policy_type > | |
357 | state_base_type; | |
358 | ||
359 | class state_iterator : public std::iterator< | |
360 | std::forward_iterator_tag, | |
361 | state_base_type, std::ptrdiff_t | |
362 | #ifndef BOOST_MSVC_STD_ITERATOR | |
363 | , const state_base_type *, const state_base_type & | |
364 | #endif | |
365 | > | |
366 | { | |
367 | public: | |
368 | ////////////////////////////////////////////////////////////////////// | |
369 | explicit state_iterator( | |
370 | typename state_base_type::state_list_type::const_iterator | |
371 | baseIterator | |
372 | ) : baseIterator_( baseIterator ) {} | |
373 | ||
374 | const state_base_type & operator*() const { return **baseIterator_; } | |
375 | const state_base_type * operator->() const | |
376 | { | |
377 | return &**baseIterator_; | |
378 | } | |
379 | ||
380 | state_iterator & operator++() { ++baseIterator_; return *this; } | |
381 | state_iterator operator++( int ) | |
382 | { | |
383 | return state_iterator( baseIterator_++ ); | |
384 | } | |
385 | ||
386 | bool operator==( const state_iterator & right ) const | |
387 | { | |
388 | return baseIterator_ == right.baseIterator_; | |
389 | } | |
390 | bool operator!=( const state_iterator & right ) const | |
391 | { | |
392 | return !( *this == right ); | |
393 | } | |
394 | ||
395 | private: | |
396 | typename state_base_type::state_list_type::const_iterator | |
397 | baseIterator_; | |
398 | }; | |
399 | ||
400 | state_iterator state_begin() const | |
401 | { | |
402 | return state_iterator( currentStates_.begin() ); | |
403 | } | |
404 | ||
405 | state_iterator state_end() const | |
406 | { | |
407 | return state_iterator( currentStatesEnd_ ); | |
408 | } | |
409 | ||
410 | void unconsumed_event( const event_base & ) {} | |
411 | ||
412 | protected: | |
413 | ////////////////////////////////////////////////////////////////////////// | |
414 | state_machine() : | |
415 | currentStatesEnd_( currentStates_.end() ), | |
416 | pOutermostState_( 0 ), | |
417 | isInnermostCommonOuter_( false ), | |
418 | performFullExit_( true ), | |
419 | pTriggeringEvent_( 0 ) | |
420 | { | |
421 | } | |
422 | ||
423 | // This destructor was only made virtual so that that | |
424 | // polymorphic_downcast can be used to cast to MostDerived. | |
425 | virtual ~state_machine() | |
426 | { | |
427 | terminate_impl( false ); | |
428 | } | |
429 | ||
430 | void post_event( const event_base_ptr_type & pEvent ) | |
431 | { | |
432 | post_event_impl( pEvent ); | |
433 | } | |
434 | ||
435 | void post_event( const event_base & evt ) | |
436 | { | |
437 | post_event_impl( evt ); | |
438 | } | |
439 | ||
440 | public: | |
441 | ////////////////////////////////////////////////////////////////////////// | |
442 | // The following declarations should be protected. | |
443 | // They are only public because many compilers lack template friends. | |
444 | ////////////////////////////////////////////////////////////////////////// | |
445 | template< | |
446 | class HistoryContext, | |
447 | detail::orthogonal_position_type orthogonalPosition > | |
448 | void clear_shallow_history() | |
449 | { | |
450 | // If you receive a | |
451 | // "use of undefined type 'boost::STATIC_ASSERTION_FAILURE<x>'" or | |
452 | // similar compiler error here then you tried to clear shallow history | |
453 | // for a state that does not have shallow history. That is, the state | |
454 | // does not pass either statechart::has_shallow_history or | |
455 | // statechart::has_full_history to its base class template. | |
456 | BOOST_STATIC_ASSERT( HistoryContext::shallow_history::value ); | |
457 | ||
458 | typedef typename mpl::at_c< | |
459 | typename HistoryContext::inner_initial_list, | |
460 | orthogonalPosition >::type historized_state; | |
461 | ||
462 | store_history_impl( | |
463 | shallowHistoryMap_, | |
464 | history_key_type::make_history_key< historized_state >(), | |
465 | 0 ); | |
466 | } | |
467 | ||
468 | template< | |
469 | class HistoryContext, | |
470 | detail::orthogonal_position_type orthogonalPosition > | |
471 | void clear_deep_history() | |
472 | { | |
473 | // If you receive a | |
474 | // "use of undefined type 'boost::STATIC_ASSERTION_FAILURE<x>'" or | |
475 | // similar compiler error here then you tried to clear deep history for | |
476 | // a state that does not have deep history. That is, the state does not | |
477 | // pass either statechart::has_deep_history or | |
478 | // statechart::has_full_history to its base class template | |
479 | BOOST_STATIC_ASSERT( HistoryContext::deep_history::value ); | |
480 | ||
481 | typedef typename mpl::at_c< | |
482 | typename HistoryContext::inner_initial_list, | |
483 | orthogonalPosition >::type historized_state; | |
484 | ||
485 | store_history_impl( | |
486 | deepHistoryMap_, | |
487 | history_key_type::make_history_key< historized_state >(), | |
488 | 0 ); | |
489 | } | |
490 | ||
491 | const event_base_type * triggering_event() const | |
492 | { | |
493 | return pTriggeringEvent_; | |
494 | } | |
495 | ||
496 | public: | |
497 | ////////////////////////////////////////////////////////////////////////// | |
498 | // The following declarations should be private. | |
499 | // They are only public because many compilers lack template friends. | |
500 | ////////////////////////////////////////////////////////////////////////// | |
501 | typedef MostDerived inner_context_type; | |
502 | typedef mpl::integral_c< detail::orthogonal_position_type, 0 > | |
503 | inner_orthogonal_position; | |
504 | typedef mpl::integral_c< detail::orthogonal_position_type, 1 > | |
505 | no_of_orthogonal_regions; | |
506 | ||
507 | typedef MostDerived outermost_context_type; | |
508 | typedef state_machine outermost_context_base_type; | |
509 | typedef state_machine * inner_context_ptr_type; | |
510 | typedef typename state_base_type::node_state_base_ptr_type | |
511 | node_state_base_ptr_type; | |
512 | typedef typename state_base_type::leaf_state_ptr_type leaf_state_ptr_type; | |
513 | typedef typename state_base_type::state_list_type state_list_type; | |
514 | ||
515 | typedef mpl::clear< mpl::list<> >::type context_type_list; | |
516 | ||
517 | typedef mpl::bool_< false > shallow_history; | |
518 | typedef mpl::bool_< false > deep_history; | |
519 | typedef mpl::bool_< false > inherited_deep_history; | |
520 | ||
521 | void post_event_impl( const event_base_ptr_type & pEvent ) | |
522 | { | |
523 | BOOST_ASSERT( get_pointer( pEvent ) != 0 ); | |
524 | eventQueue_.push_back( pEvent ); | |
525 | } | |
526 | ||
527 | void post_event_impl( const event_base & evt ) | |
528 | { | |
529 | post_event_impl( evt.intrusive_from_this() ); | |
530 | } | |
531 | ||
532 | detail::reaction_result react_impl( | |
533 | const event_base_type &, | |
534 | typename rtti_policy_type::id_type ) | |
535 | { | |
536 | return detail::do_forward_event; | |
537 | } | |
538 | ||
539 | void exit_impl( | |
540 | inner_context_ptr_type &, | |
541 | typename state_base_type::node_state_base_ptr_type &, | |
542 | bool ) {} | |
543 | ||
544 | void set_outermost_unstable_state( | |
545 | typename state_base_type::node_state_base_ptr_type & | |
546 | pOutermostUnstableState ) | |
547 | { | |
548 | pOutermostUnstableState = 0; | |
549 | } | |
550 | ||
551 | // Returns a reference to the context identified by the template | |
552 | // parameter. This can either be _this_ object or one of its direct or | |
553 | // indirect contexts. | |
554 | template< class Context > | |
555 | Context & context() | |
556 | { | |
557 | // As we are in the outermost context here, only this object can be | |
558 | // returned. | |
559 | return *polymorphic_downcast< MostDerived * >( this ); | |
560 | } | |
561 | ||
562 | template< class Context > | |
563 | const Context & context() const | |
564 | { | |
565 | // As we are in the outermost context here, only this object can be | |
566 | // returned. | |
567 | return *polymorphic_downcast< const MostDerived * >( this ); | |
568 | } | |
569 | ||
570 | outermost_context_type & outermost_context() | |
571 | { | |
572 | return *polymorphic_downcast< MostDerived * >( this ); | |
573 | } | |
574 | ||
575 | const outermost_context_type & outermost_context() const | |
576 | { | |
577 | return *polymorphic_downcast< const MostDerived * >( this ); | |
578 | } | |
579 | ||
580 | outermost_context_base_type & outermost_context_base() | |
581 | { | |
582 | return *this; | |
583 | } | |
584 | ||
585 | const outermost_context_base_type & outermost_context_base() const | |
586 | { | |
587 | return *this; | |
588 | } | |
589 | ||
590 | void terminate_as_reaction( state_base_type & theState ) | |
591 | { | |
592 | terminate_impl( theState, performFullExit_ ); | |
593 | pOutermostUnstableState_ = 0; | |
594 | } | |
595 | ||
596 | void terminate_as_part_of_transit( state_base_type & theState ) | |
597 | { | |
598 | terminate_impl( theState, performFullExit_ ); | |
599 | isInnermostCommonOuter_ = true; | |
600 | } | |
601 | ||
602 | void terminate_as_part_of_transit( state_machine & ) | |
603 | { | |
604 | terminate_impl( *pOutermostState_, performFullExit_ ); | |
605 | isInnermostCommonOuter_ = true; | |
606 | } | |
607 | ||
608 | ||
609 | template< class State > | |
610 | void add( const intrusive_ptr< State > & pState ) | |
611 | { | |
612 | // The second dummy argument is necessary because the call to the | |
613 | // overloaded function add_impl would otherwise be ambiguous. | |
614 | node_state_base_ptr_type pNewOutermostUnstableStateCandidate = | |
615 | add_impl( pState, *pState ); | |
616 | ||
617 | if ( isInnermostCommonOuter_ || | |
618 | ( is_in_highest_orthogonal_region< State >() && | |
619 | ( get_pointer( pOutermostUnstableState_ ) == | |
620 | pState->State::outer_state_ptr() ) ) ) | |
621 | { | |
622 | isInnermostCommonOuter_ = false; | |
623 | pOutermostUnstableState_ = pNewOutermostUnstableStateCandidate; | |
624 | } | |
625 | } | |
626 | ||
627 | ||
628 | void add_inner_state( | |
629 | detail::orthogonal_position_type position, | |
630 | state_base_type * pOutermostState ) | |
631 | { | |
632 | BOOST_ASSERT( position == 0 ); | |
633 | detail::avoid_unused_warning( position ); | |
634 | pOutermostState_ = pOutermostState; | |
635 | } | |
636 | ||
637 | void remove_inner_state( detail::orthogonal_position_type position ) | |
638 | { | |
639 | BOOST_ASSERT( position == 0 ); | |
640 | detail::avoid_unused_warning( position ); | |
641 | pOutermostState_ = 0; | |
642 | } | |
643 | ||
644 | ||
645 | void release_events() | |
646 | { | |
647 | eventQueue_.splice( eventQueue_.begin(), deferredEventQueue_ ); | |
648 | } | |
649 | ||
650 | ||
651 | template< class HistorizedState > | |
652 | void store_shallow_history() | |
653 | { | |
654 | // 5.2.10.6 declares that reinterpret_casting a function pointer to a | |
655 | // different function pointer and back must yield the same value. The | |
656 | // following reinterpret_cast is the first half of such a sequence. | |
657 | store_history_impl( | |
658 | shallowHistoryMap_, | |
659 | history_key_type::make_history_key< HistorizedState >(), | |
660 | reinterpret_cast< void (*)() >( &HistorizedState::deep_construct ) ); | |
661 | } | |
662 | ||
663 | template< class DefaultState > | |
664 | void construct_with_shallow_history( | |
665 | const typename DefaultState::context_ptr_type & pContext ) | |
666 | { | |
667 | construct_with_history_impl< DefaultState >( | |
668 | shallowHistoryMap_, pContext ); | |
669 | } | |
670 | ||
671 | ||
672 | template< class HistorizedState, class LeafState > | |
673 | void store_deep_history() | |
674 | { | |
675 | typedef typename detail::make_context_list< | |
676 | typename HistorizedState::context_type, | |
677 | LeafState >::type history_context_list; | |
678 | typedef detail::constructor< | |
679 | history_context_list, outermost_context_base_type > constructor_type; | |
680 | // 5.2.10.6 declares that reinterpret_casting a function pointer to a | |
681 | // different function pointer and back must yield the same value. The | |
682 | // following reinterpret_cast is the first half of such a sequence. | |
683 | store_history_impl( | |
684 | deepHistoryMap_, | |
685 | history_key_type::make_history_key< HistorizedState >(), | |
686 | reinterpret_cast< void (*)() >( &constructor_type::construct ) ); | |
687 | } | |
688 | ||
689 | template< class DefaultState > | |
690 | void construct_with_deep_history( | |
691 | const typename DefaultState::context_ptr_type & pContext ) | |
692 | { | |
693 | construct_with_history_impl< DefaultState >( | |
694 | deepHistoryMap_, pContext ); | |
695 | } | |
696 | ||
697 | private: // implementation | |
698 | ////////////////////////////////////////////////////////////////////////// | |
699 | void initial_construct() | |
700 | { | |
701 | InitialState::initial_deep_construct( | |
702 | *polymorphic_downcast< MostDerived * >( this ) ); | |
703 | } | |
704 | ||
705 | class initial_construct_function | |
706 | { | |
707 | public: | |
708 | ////////////////////////////////////////////////////////////////////// | |
709 | initial_construct_function( state_machine & machine ) : | |
710 | machine_( machine ) | |
711 | { | |
712 | } | |
713 | ||
714 | result operator()() | |
715 | { | |
716 | machine_.initial_construct(); | |
717 | return detail::result_utility::make_result( | |
718 | detail::do_discard_event ); // there is nothing to be consumed | |
719 | } | |
720 | ||
721 | private: | |
722 | ////////////////////////////////////////////////////////////////////// | |
723 | // avoids C4512 (assignment operator could not be generated) | |
724 | initial_construct_function & operator=( | |
725 | const initial_construct_function & ); | |
726 | ||
727 | state_machine & machine_; | |
728 | }; | |
729 | friend class initial_construct_function; | |
730 | ||
731 | class terminate_function | |
732 | { | |
733 | public: | |
734 | ////////////////////////////////////////////////////////////////////// | |
735 | terminate_function( state_machine & machine ) : machine_( machine ) {} | |
736 | ||
737 | result operator()() | |
738 | { | |
739 | machine_.terminate_impl( true ); | |
740 | return detail::result_utility::make_result( | |
741 | detail::do_discard_event ); // there is nothing to be consumed | |
742 | } | |
743 | ||
744 | private: | |
745 | ////////////////////////////////////////////////////////////////////// | |
746 | // avoids C4512 (assignment operator could not be generated) | |
747 | terminate_function & operator=( const terminate_function & ); | |
748 | ||
749 | state_machine & machine_; | |
750 | }; | |
751 | friend class terminate_function; | |
752 | ||
753 | template< class ExceptionEvent > | |
754 | detail::reaction_result handle_exception_event( | |
755 | const ExceptionEvent & exceptionEvent, | |
756 | state_base_type * pCurrentState ) | |
757 | { | |
758 | if ( terminated() ) | |
759 | { | |
760 | // there is no state that could handle the exception -> bail out | |
761 | throw; | |
762 | } | |
763 | ||
764 | // If we are stable, an event handler has thrown. | |
765 | // Otherwise, either a state constructor, a transition action or an exit | |
766 | // function has thrown and the state machine is now in an invalid state. | |
767 | // This situation can be resolved by the exception event handler | |
768 | // function by orderly transiting to another state or terminating. | |
769 | // As a result of this, the machine must not be unstable when this | |
770 | // function is left. | |
771 | state_base_type * const pOutermostUnstableState = | |
772 | get_pointer( pOutermostUnstableState_ ); | |
773 | state_base_type * const pHandlingState = pOutermostUnstableState == 0 ? | |
774 | pCurrentState : pOutermostUnstableState; | |
775 | ||
776 | BOOST_ASSERT( pHandlingState != 0 ); | |
777 | terminator guard( *this, &exceptionEvent ); | |
778 | // There is another scope guard up the call stack, which will terminate | |
779 | // the machine. So this guard only sets the triggering event. | |
780 | guard.dismiss(); | |
781 | ||
782 | // Setting a member variable to a special value for the duration of a | |
783 | // call surely looks like a kludge (normally it should be a parameter of | |
784 | // the call). However, in this case it is unavoidable because the call | |
785 | // below could result in a call to user code where passing through an | |
786 | // additional bool parameter is not acceptable. | |
787 | performFullExit_ = false; | |
788 | const detail::reaction_result reactionResult = pHandlingState->react_impl( | |
789 | exceptionEvent, exceptionEvent.dynamic_type() ); | |
790 | // If the above call throws then performFullExit_ will obviously not be | |
791 | // set back to true. In this case the termination triggered by the | |
792 | // scope guard further up in the call stack will take care of this. | |
793 | performFullExit_ = true; | |
794 | ||
795 | if ( ( reactionResult != detail::do_discard_event ) || | |
796 | ( get_pointer( pOutermostUnstableState_ ) != 0 ) ) | |
797 | { | |
798 | throw; | |
799 | } | |
800 | ||
801 | return detail::do_discard_event; | |
802 | } | |
803 | ||
804 | class exception_event_handler | |
805 | { | |
806 | public: | |
807 | ////////////////////////////////////////////////////////////////////// | |
808 | exception_event_handler( | |
809 | state_machine & machine, | |
810 | state_base_type * pCurrentState = 0 | |
811 | ) : | |
812 | machine_( machine ), | |
813 | pCurrentState_( pCurrentState ) | |
814 | { | |
815 | } | |
816 | ||
817 | template< class ExceptionEvent > | |
818 | result operator()( | |
819 | const ExceptionEvent & exceptionEvent ) | |
820 | { | |
821 | return detail::result_utility::make_result( | |
822 | machine_.handle_exception_event( | |
823 | exceptionEvent, pCurrentState_ ) ); | |
824 | } | |
825 | ||
826 | private: | |
827 | ////////////////////////////////////////////////////////////////////// | |
828 | // avoids C4512 (assignment operator could not be generated) | |
829 | exception_event_handler & operator=( | |
830 | const exception_event_handler & ); | |
831 | ||
832 | state_machine & machine_; | |
833 | state_base_type * pCurrentState_; | |
834 | }; | |
835 | friend class exception_event_handler; | |
836 | ||
837 | class terminator | |
838 | { | |
839 | public: | |
840 | ////////////////////////////////////////////////////////////////////// | |
841 | terminator( | |
842 | state_machine & machine, const event_base * pNewTriggeringEvent ) : | |
843 | machine_( machine ), | |
844 | pOldTriggeringEvent_(machine_.pTriggeringEvent_), | |
845 | dismissed_( false ) | |
846 | { | |
847 | machine_.pTriggeringEvent_ = pNewTriggeringEvent; | |
848 | } | |
849 | ||
850 | ~terminator() | |
851 | { | |
852 | if ( !dismissed_ ) { machine_.terminate_impl( false ); } | |
853 | machine_.pTriggeringEvent_ = pOldTriggeringEvent_; | |
854 | } | |
855 | ||
856 | void dismiss() { dismissed_ = true; } | |
857 | ||
858 | private: | |
859 | ////////////////////////////////////////////////////////////////////// | |
860 | // avoids C4512 (assignment operator could not be generated) | |
861 | terminator & operator=( const terminator & ); | |
862 | ||
863 | state_machine & machine_; | |
864 | const event_base_type * const pOldTriggeringEvent_; | |
865 | bool dismissed_; | |
866 | }; | |
867 | friend class terminator; | |
868 | ||
869 | ||
870 | detail::reaction_result send_event( const event_base_type & evt ) | |
871 | { | |
872 | terminator guard( *this, &evt ); | |
873 | BOOST_ASSERT( get_pointer( pOutermostUnstableState_ ) == 0 ); | |
874 | const typename rtti_policy_type::id_type eventType = evt.dynamic_type(); | |
875 | detail::reaction_result reactionResult = detail::do_forward_event; | |
876 | ||
877 | for ( | |
878 | typename state_list_type::iterator pState = currentStates_.begin(); | |
879 | ( reactionResult == detail::do_forward_event ) && | |
880 | ( pState != currentStatesEnd_ ); | |
881 | ++pState ) | |
882 | { | |
883 | // CAUTION: The following statement could modify our state list! | |
884 | // We must not continue iterating if the event was consumed | |
885 | reactionResult = detail::result_utility::get_result( translator_( | |
886 | detail::send_function< | |
887 | state_base_type, event_base_type, rtti_policy_type::id_type >( | |
888 | **pState, evt, eventType ), | |
889 | exception_event_handler( *this, get_pointer( *pState ) ) ) ); | |
890 | } | |
891 | ||
892 | guard.dismiss(); | |
893 | ||
894 | if ( reactionResult == detail::do_forward_event ) | |
895 | { | |
896 | polymorphic_downcast< MostDerived * >( this )->unconsumed_event( evt ); | |
897 | } | |
898 | ||
899 | return reactionResult; | |
900 | } | |
901 | ||
902 | ||
903 | void process_queued_events() | |
904 | { | |
905 | while ( !eventQueue_.empty() ) | |
906 | { | |
907 | event_base_ptr_type pEvent = eventQueue_.front(); | |
908 | eventQueue_.pop_front(); | |
909 | ||
910 | if ( send_event( *pEvent ) == detail::do_defer_event ) | |
911 | { | |
912 | deferredEventQueue_.push_back( pEvent ); | |
913 | } | |
914 | } | |
915 | } | |
916 | ||
917 | ||
918 | void terminate_impl( bool performFullExit ) | |
919 | { | |
920 | performFullExit_ = true; | |
921 | ||
922 | if ( !terminated() ) | |
923 | { | |
924 | terminate_impl( *pOutermostState_, performFullExit ); | |
925 | } | |
926 | ||
927 | eventQueue_.clear(); | |
928 | deferredEventQueue_.clear(); | |
929 | shallowHistoryMap_.clear(); | |
930 | deepHistoryMap_.clear(); | |
931 | } | |
932 | ||
933 | void terminate_impl( state_base_type & theState, bool performFullExit ) | |
934 | { | |
935 | isInnermostCommonOuter_ = false; | |
936 | ||
937 | // If pOutermostUnstableState_ == 0, we know for sure that | |
938 | // currentStates_.size() > 0, otherwise theState couldn't be alive any | |
939 | // more | |
940 | if ( get_pointer( pOutermostUnstableState_ ) != 0 ) | |
941 | { | |
942 | theState.remove_from_state_list( | |
943 | currentStatesEnd_, pOutermostUnstableState_, performFullExit ); | |
944 | } | |
945 | // Optimization: We want to find out whether currentStates_ has size 1 | |
946 | // and if yes use the optimized implementation below. Since | |
947 | // list<>::size() is implemented quite inefficiently in some std libs | |
948 | // it is best to just decrement the currentStatesEnd_ here and | |
949 | // increment it again, if the test failed. | |
950 | else if ( currentStates_.begin() == --currentStatesEnd_ ) | |
951 | { | |
952 | // The machine is stable and there is exactly one innermost state. | |
953 | // The following optimization is only correct for a stable machine | |
954 | // without orthogonal regions. | |
955 | leaf_state_ptr_type & pState = *currentStatesEnd_; | |
956 | pState->exit_impl( | |
957 | pState, pOutermostUnstableState_, performFullExit ); | |
958 | } | |
959 | else | |
960 | { | |
961 | BOOST_ASSERT( currentStates_.size() > 1 ); | |
962 | // The machine is stable and there are multiple innermost states | |
963 | theState.remove_from_state_list( | |
964 | ++currentStatesEnd_, pOutermostUnstableState_, performFullExit ); | |
965 | } | |
966 | } | |
967 | ||
968 | ||
969 | node_state_base_ptr_type add_impl( | |
970 | const leaf_state_ptr_type & pState, | |
971 | detail::leaf_state< allocator_type, rtti_policy_type > & ) | |
972 | { | |
973 | if ( currentStatesEnd_ == currentStates_.end() ) | |
974 | { | |
975 | pState->set_list_position( | |
976 | currentStates_.insert( currentStatesEnd_, pState ) ); | |
977 | } | |
978 | else | |
979 | { | |
980 | *currentStatesEnd_ = pState; | |
981 | pState->set_list_position( currentStatesEnd_ ); | |
982 | ++currentStatesEnd_; | |
983 | } | |
984 | ||
985 | return 0; | |
986 | } | |
987 | ||
988 | node_state_base_ptr_type add_impl( | |
989 | const node_state_base_ptr_type & pState, | |
990 | state_base_type & ) | |
991 | { | |
992 | return pState; | |
993 | } | |
994 | ||
995 | template< class State > | |
996 | static bool is_in_highest_orthogonal_region() | |
997 | { | |
998 | return mpl::equal_to< | |
999 | typename State::orthogonal_position, | |
1000 | mpl::minus< | |
1001 | typename State::context_type::no_of_orthogonal_regions, | |
1002 | mpl::integral_c< detail::orthogonal_position_type, 1 > > | |
1003 | >::value; | |
1004 | } | |
1005 | ||
1006 | ||
1007 | typedef detail::history_key< rtti_policy_type > history_key_type; | |
1008 | ||
1009 | typedef std::map< | |
1010 | history_key_type, void (*)(), | |
1011 | std::less< history_key_type >, | |
1012 | typename boost::detail::allocator::rebind_to< | |
1013 | allocator_type, std::pair< const history_key_type, void (*)() > | |
1014 | >::type | |
1015 | > history_map_type; | |
1016 | ||
1017 | void store_history_impl( | |
1018 | history_map_type & historyMap, | |
1019 | const history_key_type & historyId, | |
1020 | void (*pConstructFunction)() ) | |
1021 | { | |
1022 | historyMap[ historyId ] = pConstructFunction; | |
1023 | } | |
1024 | ||
1025 | template< class DefaultState > | |
1026 | void construct_with_history_impl( | |
1027 | history_map_type & historyMap, | |
1028 | const typename DefaultState::context_ptr_type & pContext ) | |
1029 | { | |
1030 | typename history_map_type::iterator pFoundSlot = historyMap.find( | |
1031 | history_key_type::make_history_key< DefaultState >() ); | |
1032 | ||
1033 | if ( ( pFoundSlot == historyMap.end() ) || ( pFoundSlot->second == 0 ) ) | |
1034 | { | |
1035 | // We have never entered this state before or history was cleared | |
1036 | DefaultState::deep_construct( | |
1037 | pContext, *polymorphic_downcast< MostDerived * >( this ) ); | |
1038 | } | |
1039 | else | |
1040 | { | |
1041 | typedef void construct_function( | |
1042 | const typename DefaultState::context_ptr_type &, | |
1043 | typename DefaultState::outermost_context_base_type & ); | |
1044 | // 5.2.10.6 declares that reinterpret_casting a function pointer to a | |
1045 | // different function pointer and back must yield the same value. The | |
1046 | // following reinterpret_cast is the second half of such a sequence. | |
1047 | construct_function * const pConstructFunction = | |
1048 | reinterpret_cast< construct_function * >( pFoundSlot->second ); | |
1049 | (*pConstructFunction)( | |
1050 | pContext, *polymorphic_downcast< MostDerived * >( this ) ); | |
1051 | } | |
1052 | } | |
1053 | ||
1054 | typedef std::list< | |
1055 | event_base_ptr_type, | |
1056 | typename boost::detail::allocator::rebind_to< | |
1057 | allocator_type, event_base_ptr_type >::type | |
1058 | > event_queue_type; | |
1059 | ||
1060 | typedef std::map< | |
1061 | const state_base_type *, event_queue_type, | |
1062 | std::less< const state_base_type * >, | |
1063 | typename boost::detail::allocator::rebind_to< | |
1064 | allocator_type, | |
1065 | std::pair< const state_base_type * const, event_queue_type > | |
1066 | >::type | |
1067 | > deferred_map_type; | |
1068 | ||
1069 | ||
1070 | event_queue_type eventQueue_; | |
1071 | event_queue_type deferredEventQueue_; | |
1072 | state_list_type currentStates_; | |
1073 | typename state_list_type::iterator currentStatesEnd_; | |
1074 | state_base_type * pOutermostState_; | |
1075 | bool isInnermostCommonOuter_; | |
1076 | node_state_base_ptr_type pOutermostUnstableState_; | |
1077 | ExceptionTranslator translator_; | |
1078 | bool performFullExit_; | |
1079 | history_map_type shallowHistoryMap_; | |
1080 | history_map_type deepHistoryMap_; | |
1081 | const event_base_type * pTriggeringEvent_; | |
1082 | }; | |
1083 | ||
1084 | ||
1085 | ||
1086 | } // namespace statechart | |
1087 | } // namespace boost | |
1088 | ||
1089 | ||
1090 | ||
1091 | #endif |