You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and dots ('.'), can be up to 35 characters long. Letters must be lowercase.
1091 lines
33 KiB
1091 lines
33 KiB
#ifndef BOOST_STATECHART_STATE_MACHINE_HPP_INCLUDED |
|
#define BOOST_STATECHART_STATE_MACHINE_HPP_INCLUDED |
|
////////////////////////////////////////////////////////////////////////////// |
|
// Copyright 2002-2010 Andreas Huber Doenni |
|
// Distributed under the Boost Software License, Version 1.0. (See accompany- |
|
// ing file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) |
|
////////////////////////////////////////////////////////////////////////////// |
|
|
|
|
|
|
|
#include <boost/statechart/event.hpp> |
|
#include <boost/statechart/null_exception_translator.hpp> |
|
#include <boost/statechart/result.hpp> |
|
|
|
#include <boost/statechart/detail/rtti_policy.hpp> |
|
#include <boost/statechart/detail/state_base.hpp> |
|
#include <boost/statechart/detail/leaf_state.hpp> |
|
#include <boost/statechart/detail/node_state.hpp> |
|
#include <boost/statechart/detail/constructor.hpp> |
|
#include <boost/statechart/detail/avoid_unused_warning.hpp> |
|
|
|
#include <boost/mpl/list.hpp> |
|
#include <boost/mpl/clear.hpp> |
|
#include <boost/mpl/if.hpp> |
|
#include <boost/mpl/at.hpp> |
|
#include <boost/mpl/integral_c.hpp> |
|
#include <boost/mpl/minus.hpp> |
|
#include <boost/mpl/equal_to.hpp> |
|
|
|
#include <boost/intrusive_ptr.hpp> |
|
#include <boost/type_traits/is_pointer.hpp> |
|
#include <boost/type_traits/remove_reference.hpp> |
|
#include <boost/noncopyable.hpp> |
|
#include <boost/assert.hpp> |
|
#include <boost/static_assert.hpp> |
|
#include <boost/cast.hpp> // boost::polymorphic_downcast |
|
// BOOST_NO_EXCEPTIONS, BOOST_MSVC, BOOST_MSVC_STD_ITERATOR |
|
#include <boost/config.hpp> |
|
|
|
#include <boost/detail/allocator_utilities.hpp> |
|
|
|
#ifdef BOOST_MSVC |
|
# pragma warning( push ) |
|
# pragma warning( disable: 4702 ) // unreachable code (in release mode only) |
|
#endif |
|
|
|
#include <map> |
|
|
|
#ifdef BOOST_MSVC |
|
# pragma warning( pop ) |
|
#endif |
|
|
|
#include <memory> // std::allocator |
|
#include <typeinfo> // std::bad_cast |
|
#include <functional> // std::less |
|
#include <iterator> |
|
|
|
|
|
|
|
namespace boost |
|
{ |
|
namespace statechart |
|
{ |
|
namespace detail |
|
{ |
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////// |
|
template< class StateBaseType, class EventBaseType, class IdType > |
|
class send_function |
|
{ |
|
public: |
|
////////////////////////////////////////////////////////////////////////// |
|
send_function( |
|
StateBaseType & toState, |
|
const EventBaseType & evt, |
|
IdType eventType |
|
) : |
|
toState_( toState ), evt_( evt ), eventType_( eventType ) |
|
{ |
|
} |
|
|
|
result operator()() |
|
{ |
|
return detail::result_utility::make_result( |
|
toState_.react_impl( evt_, eventType_ ) ); |
|
} |
|
|
|
private: |
|
////////////////////////////////////////////////////////////////////////// |
|
// avoids C4512 (assignment operator could not be generated) |
|
send_function & operator=( const send_function & ); |
|
|
|
StateBaseType & toState_; |
|
const EventBaseType & evt_; |
|
IdType eventType_; |
|
}; |
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////// |
|
struct state_cast_impl_pointer_target |
|
{ |
|
public: |
|
////////////////////////////////////////////////////////////////////////// |
|
template< class StateBaseType > |
|
static const StateBaseType * deref_if_necessary( |
|
const StateBaseType * pState ) |
|
{ |
|
return pState; |
|
} |
|
|
|
template< class Target, class IdType > |
|
static IdType type_id() |
|
{ |
|
Target p = 0; |
|
return type_id_impl< IdType >( p ); |
|
} |
|
|
|
static bool found( const void * pFound ) |
|
{ |
|
return pFound != 0; |
|
} |
|
|
|
template< class Target > |
|
static Target not_found() |
|
{ |
|
return 0; |
|
} |
|
|
|
private: |
|
////////////////////////////////////////////////////////////////////////// |
|
template< class IdType, class Type > |
|
static IdType type_id_impl( const Type * ) |
|
{ |
|
return Type::static_type(); |
|
} |
|
}; |
|
|
|
struct state_cast_impl_reference_target |
|
{ |
|
template< class StateBaseType > |
|
static const StateBaseType & deref_if_necessary( |
|
const StateBaseType * pState ) |
|
{ |
|
return *pState; |
|
} |
|
|
|
template< class Target, class IdType > |
|
static IdType type_id() |
|
{ |
|
return remove_reference< Target >::type::static_type(); |
|
} |
|
|
|
template< class Dummy > |
|
static bool found( const Dummy & ) |
|
{ |
|
return true; |
|
} |
|
|
|
template< class Target > |
|
static Target not_found() |
|
{ |
|
throw std::bad_cast(); |
|
} |
|
}; |
|
|
|
template< class Target > |
|
struct state_cast_impl : public mpl::if_< |
|
is_pointer< Target >, |
|
state_cast_impl_pointer_target, |
|
state_cast_impl_reference_target |
|
>::type {}; |
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////// |
|
template< class RttiPolicy > |
|
class history_key |
|
{ |
|
public: |
|
////////////////////////////////////////////////////////////////////////// |
|
template< class HistorizedState > |
|
static history_key make_history_key() |
|
{ |
|
return history_key( |
|
HistorizedState::context_type::static_type(), |
|
HistorizedState::orthogonal_position::value ); |
|
} |
|
|
|
typename RttiPolicy::id_type history_context_type() const |
|
{ |
|
return historyContextType_; |
|
} |
|
|
|
friend bool operator<( |
|
const history_key & left, const history_key & right ) |
|
{ |
|
return |
|
std::less< typename RttiPolicy::id_type >()( |
|
left.historyContextType_, right.historyContextType_ ) || |
|
( ( left.historyContextType_ == right.historyContextType_ ) && |
|
( left.historizedOrthogonalRegion_ < |
|
right.historizedOrthogonalRegion_ ) ); |
|
} |
|
|
|
private: |
|
////////////////////////////////////////////////////////////////////////// |
|
history_key( |
|
typename RttiPolicy::id_type historyContextType, |
|
orthogonal_position_type historizedOrthogonalRegion |
|
) : |
|
historyContextType_( historyContextType ), |
|
historizedOrthogonalRegion_( historizedOrthogonalRegion ) |
|
{ |
|
} |
|
|
|
// avoids C4512 (assignment operator could not be generated) |
|
history_key & operator=( const history_key & ); |
|
|
|
const typename RttiPolicy::id_type historyContextType_; |
|
const orthogonal_position_type historizedOrthogonalRegion_; |
|
}; |
|
|
|
|
|
|
|
} // namespace detail |
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////// |
|
template< class MostDerived, |
|
class InitialState, |
|
class Allocator = std::allocator< void >, |
|
class ExceptionTranslator = null_exception_translator > |
|
class state_machine : noncopyable |
|
{ |
|
public: |
|
////////////////////////////////////////////////////////////////////////// |
|
typedef Allocator allocator_type; |
|
typedef detail::rtti_policy rtti_policy_type; |
|
typedef event_base event_base_type; |
|
typedef intrusive_ptr< const event_base_type > event_base_ptr_type; |
|
|
|
void initiate() |
|
{ |
|
terminate(); |
|
|
|
{ |
|
terminator guard( *this, 0 ); |
|
detail::result_utility::get_result( translator_( |
|
initial_construct_function( *this ), |
|
exception_event_handler( *this ) ) ); |
|
guard.dismiss(); |
|
} |
|
|
|
process_queued_events(); |
|
} |
|
|
|
void terminate() |
|
{ |
|
terminator guard( *this, 0 ); |
|
detail::result_utility::get_result( translator_( |
|
terminate_function( *this ), |
|
exception_event_handler( *this ) ) ); |
|
guard.dismiss(); |
|
} |
|
|
|
bool terminated() const |
|
{ |
|
return pOutermostState_ == 0; |
|
} |
|
|
|
void process_event( const event_base_type & evt ) |
|
{ |
|
if ( send_event( evt ) == detail::do_defer_event ) |
|
{ |
|
deferredEventQueue_.push_back( evt.intrusive_from_this() ); |
|
} |
|
|
|
process_queued_events(); |
|
} |
|
|
|
template< class Target > |
|
Target state_cast() const |
|
{ |
|
typedef detail::state_cast_impl< Target > impl; |
|
|
|
for ( typename state_list_type::const_iterator pCurrentLeafState = |
|
currentStates_.begin(); |
|
pCurrentLeafState != currentStatesEnd_; |
|
++pCurrentLeafState ) |
|
{ |
|
const state_base_type * pCurrentState( |
|
get_pointer( *pCurrentLeafState ) ); |
|
|
|
while ( pCurrentState != 0 ) |
|
{ |
|
// The unnecessary try/catch overhead for pointer targets is |
|
// typically small compared to the cycles dynamic_cast needs |
|
#ifndef BOOST_NO_EXCEPTIONS |
|
try |
|
#endif |
|
{ |
|
Target result = dynamic_cast< Target >( |
|
impl::deref_if_necessary( pCurrentState ) ); |
|
|
|
if ( impl::found( result ) ) |
|
{ |
|
return result; |
|
} |
|
} |
|
#ifndef BOOST_NO_EXCEPTIONS |
|
// Intentionally swallow std::bad_cast exceptions. We'll throw one |
|
// ourselves when we fail to find a state that can be cast to Target |
|
catch ( const std::bad_cast & ) {} |
|
#endif |
|
|
|
pCurrentState = pCurrentState->outer_state_ptr(); |
|
} |
|
} |
|
|
|
return impl::template not_found< Target >(); |
|
} |
|
|
|
template< class Target > |
|
Target state_downcast() const |
|
{ |
|
typedef detail::state_cast_impl< Target > impl; |
|
|
|
typename rtti_policy_type::id_type targetType = |
|
impl::template type_id< Target, rtti_policy_type::id_type >(); |
|
|
|
for ( typename state_list_type::const_iterator pCurrentLeafState = |
|
currentStates_.begin(); |
|
pCurrentLeafState != currentStatesEnd_; |
|
++pCurrentLeafState ) |
|
{ |
|
const state_base_type * pCurrentState( |
|
get_pointer( *pCurrentLeafState ) ); |
|
|
|
while ( pCurrentState != 0 ) |
|
{ |
|
if ( pCurrentState->dynamic_type() == targetType ) |
|
{ |
|
return static_cast< Target >( |
|
impl::deref_if_necessary( pCurrentState ) ); |
|
} |
|
|
|
pCurrentState = pCurrentState->outer_state_ptr(); |
|
} |
|
} |
|
|
|
return impl::template not_found< Target >(); |
|
} |
|
|
|
typedef detail::state_base< allocator_type, rtti_policy_type > |
|
state_base_type; |
|
|
|
class state_iterator : public std::iterator< |
|
std::forward_iterator_tag, |
|
state_base_type, std::ptrdiff_t |
|
#ifndef BOOST_MSVC_STD_ITERATOR |
|
, const state_base_type *, const state_base_type & |
|
#endif |
|
> |
|
{ |
|
public: |
|
////////////////////////////////////////////////////////////////////// |
|
explicit state_iterator( |
|
typename state_base_type::state_list_type::const_iterator |
|
baseIterator |
|
) : baseIterator_( baseIterator ) {} |
|
|
|
const state_base_type & operator*() const { return **baseIterator_; } |
|
const state_base_type * operator->() const |
|
{ |
|
return &**baseIterator_; |
|
} |
|
|
|
state_iterator & operator++() { ++baseIterator_; return *this; } |
|
state_iterator operator++( int ) |
|
{ |
|
return state_iterator( baseIterator_++ ); |
|
} |
|
|
|
bool operator==( const state_iterator & right ) const |
|
{ |
|
return baseIterator_ == right.baseIterator_; |
|
} |
|
bool operator!=( const state_iterator & right ) const |
|
{ |
|
return !( *this == right ); |
|
} |
|
|
|
private: |
|
typename state_base_type::state_list_type::const_iterator |
|
baseIterator_; |
|
}; |
|
|
|
state_iterator state_begin() const |
|
{ |
|
return state_iterator( currentStates_.begin() ); |
|
} |
|
|
|
state_iterator state_end() const |
|
{ |
|
return state_iterator( currentStatesEnd_ ); |
|
} |
|
|
|
void unconsumed_event( const event_base & ) {} |
|
|
|
protected: |
|
////////////////////////////////////////////////////////////////////////// |
|
state_machine() : |
|
currentStatesEnd_( currentStates_.end() ), |
|
pOutermostState_( 0 ), |
|
isInnermostCommonOuter_( false ), |
|
performFullExit_( true ), |
|
pTriggeringEvent_( 0 ) |
|
{ |
|
} |
|
|
|
// This destructor was only made virtual so that that |
|
// polymorphic_downcast can be used to cast to MostDerived. |
|
virtual ~state_machine() |
|
{ |
|
terminate_impl( false ); |
|
} |
|
|
|
void post_event( const event_base_ptr_type & pEvent ) |
|
{ |
|
post_event_impl( pEvent ); |
|
} |
|
|
|
void post_event( const event_base & evt ) |
|
{ |
|
post_event_impl( evt ); |
|
} |
|
|
|
public: |
|
////////////////////////////////////////////////////////////////////////// |
|
// The following declarations should be protected. |
|
// They are only public because many compilers lack template friends. |
|
////////////////////////////////////////////////////////////////////////// |
|
template< |
|
class HistoryContext, |
|
detail::orthogonal_position_type orthogonalPosition > |
|
void clear_shallow_history() |
|
{ |
|
// If you receive a |
|
// "use of undefined type 'boost::STATIC_ASSERTION_FAILURE<x>'" or |
|
// similar compiler error here then you tried to clear shallow history |
|
// for a state that does not have shallow history. That is, the state |
|
// does not pass either statechart::has_shallow_history or |
|
// statechart::has_full_history to its base class template. |
|
BOOST_STATIC_ASSERT( HistoryContext::shallow_history::value ); |
|
|
|
typedef typename mpl::at_c< |
|
typename HistoryContext::inner_initial_list, |
|
orthogonalPosition >::type historized_state; |
|
|
|
store_history_impl( |
|
shallowHistoryMap_, |
|
history_key_type::make_history_key< historized_state >(), |
|
0 ); |
|
} |
|
|
|
template< |
|
class HistoryContext, |
|
detail::orthogonal_position_type orthogonalPosition > |
|
void clear_deep_history() |
|
{ |
|
// If you receive a |
|
// "use of undefined type 'boost::STATIC_ASSERTION_FAILURE<x>'" or |
|
// similar compiler error here then you tried to clear deep history for |
|
// a state that does not have deep history. That is, the state does not |
|
// pass either statechart::has_deep_history or |
|
// statechart::has_full_history to its base class template |
|
BOOST_STATIC_ASSERT( HistoryContext::deep_history::value ); |
|
|
|
typedef typename mpl::at_c< |
|
typename HistoryContext::inner_initial_list, |
|
orthogonalPosition >::type historized_state; |
|
|
|
store_history_impl( |
|
deepHistoryMap_, |
|
history_key_type::make_history_key< historized_state >(), |
|
0 ); |
|
} |
|
|
|
const event_base_type * triggering_event() const |
|
{ |
|
return pTriggeringEvent_; |
|
} |
|
|
|
public: |
|
////////////////////////////////////////////////////////////////////////// |
|
// The following declarations should be private. |
|
// They are only public because many compilers lack template friends. |
|
////////////////////////////////////////////////////////////////////////// |
|
typedef MostDerived inner_context_type; |
|
typedef mpl::integral_c< detail::orthogonal_position_type, 0 > |
|
inner_orthogonal_position; |
|
typedef mpl::integral_c< detail::orthogonal_position_type, 1 > |
|
no_of_orthogonal_regions; |
|
|
|
typedef MostDerived outermost_context_type; |
|
typedef state_machine outermost_context_base_type; |
|
typedef state_machine * inner_context_ptr_type; |
|
typedef typename state_base_type::node_state_base_ptr_type |
|
node_state_base_ptr_type; |
|
typedef typename state_base_type::leaf_state_ptr_type leaf_state_ptr_type; |
|
typedef typename state_base_type::state_list_type state_list_type; |
|
|
|
typedef mpl::clear< mpl::list<> >::type context_type_list; |
|
|
|
typedef mpl::bool_< false > shallow_history; |
|
typedef mpl::bool_< false > deep_history; |
|
typedef mpl::bool_< false > inherited_deep_history; |
|
|
|
void post_event_impl( const event_base_ptr_type & pEvent ) |
|
{ |
|
BOOST_ASSERT( get_pointer( pEvent ) != 0 ); |
|
eventQueue_.push_back( pEvent ); |
|
} |
|
|
|
void post_event_impl( const event_base & evt ) |
|
{ |
|
post_event_impl( evt.intrusive_from_this() ); |
|
} |
|
|
|
detail::reaction_result react_impl( |
|
const event_base_type &, |
|
typename rtti_policy_type::id_type ) |
|
{ |
|
return detail::do_forward_event; |
|
} |
|
|
|
void exit_impl( |
|
inner_context_ptr_type &, |
|
typename state_base_type::node_state_base_ptr_type &, |
|
bool ) {} |
|
|
|
void set_outermost_unstable_state( |
|
typename state_base_type::node_state_base_ptr_type & |
|
pOutermostUnstableState ) |
|
{ |
|
pOutermostUnstableState = 0; |
|
} |
|
|
|
// Returns a reference to the context identified by the template |
|
// parameter. This can either be _this_ object or one of its direct or |
|
// indirect contexts. |
|
template< class Context > |
|
Context & context() |
|
{ |
|
// As we are in the outermost context here, only this object can be |
|
// returned. |
|
return *polymorphic_downcast< MostDerived * >( this ); |
|
} |
|
|
|
template< class Context > |
|
const Context & context() const |
|
{ |
|
// As we are in the outermost context here, only this object can be |
|
// returned. |
|
return *polymorphic_downcast< const MostDerived * >( this ); |
|
} |
|
|
|
outermost_context_type & outermost_context() |
|
{ |
|
return *polymorphic_downcast< MostDerived * >( this ); |
|
} |
|
|
|
const outermost_context_type & outermost_context() const |
|
{ |
|
return *polymorphic_downcast< const MostDerived * >( this ); |
|
} |
|
|
|
outermost_context_base_type & outermost_context_base() |
|
{ |
|
return *this; |
|
} |
|
|
|
const outermost_context_base_type & outermost_context_base() const |
|
{ |
|
return *this; |
|
} |
|
|
|
void terminate_as_reaction( state_base_type & theState ) |
|
{ |
|
terminate_impl( theState, performFullExit_ ); |
|
pOutermostUnstableState_ = 0; |
|
} |
|
|
|
void terminate_as_part_of_transit( state_base_type & theState ) |
|
{ |
|
terminate_impl( theState, performFullExit_ ); |
|
isInnermostCommonOuter_ = true; |
|
} |
|
|
|
void terminate_as_part_of_transit( state_machine & ) |
|
{ |
|
terminate_impl( *pOutermostState_, performFullExit_ ); |
|
isInnermostCommonOuter_ = true; |
|
} |
|
|
|
|
|
template< class State > |
|
void add( const intrusive_ptr< State > & pState ) |
|
{ |
|
// The second dummy argument is necessary because the call to the |
|
// overloaded function add_impl would otherwise be ambiguous. |
|
node_state_base_ptr_type pNewOutermostUnstableStateCandidate = |
|
add_impl( pState, *pState ); |
|
|
|
if ( isInnermostCommonOuter_ || |
|
( is_in_highest_orthogonal_region< State >() && |
|
( get_pointer( pOutermostUnstableState_ ) == |
|
pState->State::outer_state_ptr() ) ) ) |
|
{ |
|
isInnermostCommonOuter_ = false; |
|
pOutermostUnstableState_ = pNewOutermostUnstableStateCandidate; |
|
} |
|
} |
|
|
|
|
|
void add_inner_state( |
|
detail::orthogonal_position_type position, |
|
state_base_type * pOutermostState ) |
|
{ |
|
BOOST_ASSERT( position == 0 ); |
|
detail::avoid_unused_warning( position ); |
|
pOutermostState_ = pOutermostState; |
|
} |
|
|
|
void remove_inner_state( detail::orthogonal_position_type position ) |
|
{ |
|
BOOST_ASSERT( position == 0 ); |
|
detail::avoid_unused_warning( position ); |
|
pOutermostState_ = 0; |
|
} |
|
|
|
|
|
void release_events() |
|
{ |
|
eventQueue_.splice( eventQueue_.begin(), deferredEventQueue_ ); |
|
} |
|
|
|
|
|
template< class HistorizedState > |
|
void store_shallow_history() |
|
{ |
|
// 5.2.10.6 declares that reinterpret_casting a function pointer to a |
|
// different function pointer and back must yield the same value. The |
|
// following reinterpret_cast is the first half of such a sequence. |
|
store_history_impl( |
|
shallowHistoryMap_, |
|
history_key_type::make_history_key< HistorizedState >(), |
|
reinterpret_cast< void (*)() >( &HistorizedState::deep_construct ) ); |
|
} |
|
|
|
template< class DefaultState > |
|
void construct_with_shallow_history( |
|
const typename DefaultState::context_ptr_type & pContext ) |
|
{ |
|
construct_with_history_impl< DefaultState >( |
|
shallowHistoryMap_, pContext ); |
|
} |
|
|
|
|
|
template< class HistorizedState, class LeafState > |
|
void store_deep_history() |
|
{ |
|
typedef typename detail::make_context_list< |
|
typename HistorizedState::context_type, |
|
LeafState >::type history_context_list; |
|
typedef detail::constructor< |
|
history_context_list, outermost_context_base_type > constructor_type; |
|
// 5.2.10.6 declares that reinterpret_casting a function pointer to a |
|
// different function pointer and back must yield the same value. The |
|
// following reinterpret_cast is the first half of such a sequence. |
|
store_history_impl( |
|
deepHistoryMap_, |
|
history_key_type::make_history_key< HistorizedState >(), |
|
reinterpret_cast< void (*)() >( &constructor_type::construct ) ); |
|
} |
|
|
|
template< class DefaultState > |
|
void construct_with_deep_history( |
|
const typename DefaultState::context_ptr_type & pContext ) |
|
{ |
|
construct_with_history_impl< DefaultState >( |
|
deepHistoryMap_, pContext ); |
|
} |
|
|
|
private: // implementation |
|
////////////////////////////////////////////////////////////////////////// |
|
void initial_construct() |
|
{ |
|
InitialState::initial_deep_construct( |
|
*polymorphic_downcast< MostDerived * >( this ) ); |
|
} |
|
|
|
class initial_construct_function |
|
{ |
|
public: |
|
////////////////////////////////////////////////////////////////////// |
|
initial_construct_function( state_machine & machine ) : |
|
machine_( machine ) |
|
{ |
|
} |
|
|
|
result operator()() |
|
{ |
|
machine_.initial_construct(); |
|
return detail::result_utility::make_result( |
|
detail::do_discard_event ); // there is nothing to be consumed |
|
} |
|
|
|
private: |
|
////////////////////////////////////////////////////////////////////// |
|
// avoids C4512 (assignment operator could not be generated) |
|
initial_construct_function & operator=( |
|
const initial_construct_function & ); |
|
|
|
state_machine & machine_; |
|
}; |
|
friend class initial_construct_function; |
|
|
|
class terminate_function |
|
{ |
|
public: |
|
////////////////////////////////////////////////////////////////////// |
|
terminate_function( state_machine & machine ) : machine_( machine ) {} |
|
|
|
result operator()() |
|
{ |
|
machine_.terminate_impl( true ); |
|
return detail::result_utility::make_result( |
|
detail::do_discard_event ); // there is nothing to be consumed |
|
} |
|
|
|
private: |
|
////////////////////////////////////////////////////////////////////// |
|
// avoids C4512 (assignment operator could not be generated) |
|
terminate_function & operator=( const terminate_function & ); |
|
|
|
state_machine & machine_; |
|
}; |
|
friend class terminate_function; |
|
|
|
template< class ExceptionEvent > |
|
detail::reaction_result handle_exception_event( |
|
const ExceptionEvent & exceptionEvent, |
|
state_base_type * pCurrentState ) |
|
{ |
|
if ( terminated() ) |
|
{ |
|
// there is no state that could handle the exception -> bail out |
|
throw; |
|
} |
|
|
|
// If we are stable, an event handler has thrown. |
|
// Otherwise, either a state constructor, a transition action or an exit |
|
// function has thrown and the state machine is now in an invalid state. |
|
// This situation can be resolved by the exception event handler |
|
// function by orderly transiting to another state or terminating. |
|
// As a result of this, the machine must not be unstable when this |
|
// function is left. |
|
state_base_type * const pOutermostUnstableState = |
|
get_pointer( pOutermostUnstableState_ ); |
|
state_base_type * const pHandlingState = pOutermostUnstableState == 0 ? |
|
pCurrentState : pOutermostUnstableState; |
|
|
|
BOOST_ASSERT( pHandlingState != 0 ); |
|
terminator guard( *this, &exceptionEvent ); |
|
// There is another scope guard up the call stack, which will terminate |
|
// the machine. So this guard only sets the triggering event. |
|
guard.dismiss(); |
|
|
|
// Setting a member variable to a special value for the duration of a |
|
// call surely looks like a kludge (normally it should be a parameter of |
|
// the call). However, in this case it is unavoidable because the call |
|
// below could result in a call to user code where passing through an |
|
// additional bool parameter is not acceptable. |
|
performFullExit_ = false; |
|
const detail::reaction_result reactionResult = pHandlingState->react_impl( |
|
exceptionEvent, exceptionEvent.dynamic_type() ); |
|
// If the above call throws then performFullExit_ will obviously not be |
|
// set back to true. In this case the termination triggered by the |
|
// scope guard further up in the call stack will take care of this. |
|
performFullExit_ = true; |
|
|
|
if ( ( reactionResult != detail::do_discard_event ) || |
|
( get_pointer( pOutermostUnstableState_ ) != 0 ) ) |
|
{ |
|
throw; |
|
} |
|
|
|
return detail::do_discard_event; |
|
} |
|
|
|
class exception_event_handler |
|
{ |
|
public: |
|
////////////////////////////////////////////////////////////////////// |
|
exception_event_handler( |
|
state_machine & machine, |
|
state_base_type * pCurrentState = 0 |
|
) : |
|
machine_( machine ), |
|
pCurrentState_( pCurrentState ) |
|
{ |
|
} |
|
|
|
template< class ExceptionEvent > |
|
result operator()( |
|
const ExceptionEvent & exceptionEvent ) |
|
{ |
|
return detail::result_utility::make_result( |
|
machine_.handle_exception_event( |
|
exceptionEvent, pCurrentState_ ) ); |
|
} |
|
|
|
private: |
|
////////////////////////////////////////////////////////////////////// |
|
// avoids C4512 (assignment operator could not be generated) |
|
exception_event_handler & operator=( |
|
const exception_event_handler & ); |
|
|
|
state_machine & machine_; |
|
state_base_type * pCurrentState_; |
|
}; |
|
friend class exception_event_handler; |
|
|
|
class terminator |
|
{ |
|
public: |
|
////////////////////////////////////////////////////////////////////// |
|
terminator( |
|
state_machine & machine, const event_base * pNewTriggeringEvent ) : |
|
machine_( machine ), |
|
pOldTriggeringEvent_(machine_.pTriggeringEvent_), |
|
dismissed_( false ) |
|
{ |
|
machine_.pTriggeringEvent_ = pNewTriggeringEvent; |
|
} |
|
|
|
~terminator() |
|
{ |
|
if ( !dismissed_ ) { machine_.terminate_impl( false ); } |
|
machine_.pTriggeringEvent_ = pOldTriggeringEvent_; |
|
} |
|
|
|
void dismiss() { dismissed_ = true; } |
|
|
|
private: |
|
////////////////////////////////////////////////////////////////////// |
|
// avoids C4512 (assignment operator could not be generated) |
|
terminator & operator=( const terminator & ); |
|
|
|
state_machine & machine_; |
|
const event_base_type * const pOldTriggeringEvent_; |
|
bool dismissed_; |
|
}; |
|
friend class terminator; |
|
|
|
|
|
detail::reaction_result send_event( const event_base_type & evt ) |
|
{ |
|
terminator guard( *this, &evt ); |
|
BOOST_ASSERT( get_pointer( pOutermostUnstableState_ ) == 0 ); |
|
const typename rtti_policy_type::id_type eventType = evt.dynamic_type(); |
|
detail::reaction_result reactionResult = detail::do_forward_event; |
|
|
|
for ( |
|
typename state_list_type::iterator pState = currentStates_.begin(); |
|
( reactionResult == detail::do_forward_event ) && |
|
( pState != currentStatesEnd_ ); |
|
++pState ) |
|
{ |
|
// CAUTION: The following statement could modify our state list! |
|
// We must not continue iterating if the event was consumed |
|
reactionResult = detail::result_utility::get_result( translator_( |
|
detail::send_function< |
|
state_base_type, event_base_type, rtti_policy_type::id_type >( |
|
**pState, evt, eventType ), |
|
exception_event_handler( *this, get_pointer( *pState ) ) ) ); |
|
} |
|
|
|
guard.dismiss(); |
|
|
|
if ( reactionResult == detail::do_forward_event ) |
|
{ |
|
polymorphic_downcast< MostDerived * >( this )->unconsumed_event( evt ); |
|
} |
|
|
|
return reactionResult; |
|
} |
|
|
|
|
|
void process_queued_events() |
|
{ |
|
while ( !eventQueue_.empty() ) |
|
{ |
|
event_base_ptr_type pEvent = eventQueue_.front(); |
|
eventQueue_.pop_front(); |
|
|
|
if ( send_event( *pEvent ) == detail::do_defer_event ) |
|
{ |
|
deferredEventQueue_.push_back( pEvent ); |
|
} |
|
} |
|
} |
|
|
|
|
|
void terminate_impl( bool performFullExit ) |
|
{ |
|
performFullExit_ = true; |
|
|
|
if ( !terminated() ) |
|
{ |
|
terminate_impl( *pOutermostState_, performFullExit ); |
|
} |
|
|
|
eventQueue_.clear(); |
|
deferredEventQueue_.clear(); |
|
shallowHistoryMap_.clear(); |
|
deepHistoryMap_.clear(); |
|
} |
|
|
|
void terminate_impl( state_base_type & theState, bool performFullExit ) |
|
{ |
|
isInnermostCommonOuter_ = false; |
|
|
|
// If pOutermostUnstableState_ == 0, we know for sure that |
|
// currentStates_.size() > 0, otherwise theState couldn't be alive any |
|
// more |
|
if ( get_pointer( pOutermostUnstableState_ ) != 0 ) |
|
{ |
|
theState.remove_from_state_list( |
|
currentStatesEnd_, pOutermostUnstableState_, performFullExit ); |
|
} |
|
// Optimization: We want to find out whether currentStates_ has size 1 |
|
// and if yes use the optimized implementation below. Since |
|
// list<>::size() is implemented quite inefficiently in some std libs |
|
// it is best to just decrement the currentStatesEnd_ here and |
|
// increment it again, if the test failed. |
|
else if ( currentStates_.begin() == --currentStatesEnd_ ) |
|
{ |
|
// The machine is stable and there is exactly one innermost state. |
|
// The following optimization is only correct for a stable machine |
|
// without orthogonal regions. |
|
leaf_state_ptr_type & pState = *currentStatesEnd_; |
|
pState->exit_impl( |
|
pState, pOutermostUnstableState_, performFullExit ); |
|
} |
|
else |
|
{ |
|
BOOST_ASSERT( currentStates_.size() > 1 ); |
|
// The machine is stable and there are multiple innermost states |
|
theState.remove_from_state_list( |
|
++currentStatesEnd_, pOutermostUnstableState_, performFullExit ); |
|
} |
|
} |
|
|
|
|
|
node_state_base_ptr_type add_impl( |
|
const leaf_state_ptr_type & pState, |
|
detail::leaf_state< allocator_type, rtti_policy_type > & ) |
|
{ |
|
if ( currentStatesEnd_ == currentStates_.end() ) |
|
{ |
|
pState->set_list_position( |
|
currentStates_.insert( currentStatesEnd_, pState ) ); |
|
} |
|
else |
|
{ |
|
*currentStatesEnd_ = pState; |
|
pState->set_list_position( currentStatesEnd_ ); |
|
++currentStatesEnd_; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
node_state_base_ptr_type add_impl( |
|
const node_state_base_ptr_type & pState, |
|
state_base_type & ) |
|
{ |
|
return pState; |
|
} |
|
|
|
template< class State > |
|
static bool is_in_highest_orthogonal_region() |
|
{ |
|
return mpl::equal_to< |
|
typename State::orthogonal_position, |
|
mpl::minus< |
|
typename State::context_type::no_of_orthogonal_regions, |
|
mpl::integral_c< detail::orthogonal_position_type, 1 > > |
|
>::value; |
|
} |
|
|
|
|
|
typedef detail::history_key< rtti_policy_type > history_key_type; |
|
|
|
typedef std::map< |
|
history_key_type, void (*)(), |
|
std::less< history_key_type >, |
|
typename boost::detail::allocator::rebind_to< |
|
allocator_type, std::pair< const history_key_type, void (*)() > |
|
>::type |
|
> history_map_type; |
|
|
|
void store_history_impl( |
|
history_map_type & historyMap, |
|
const history_key_type & historyId, |
|
void (*pConstructFunction)() ) |
|
{ |
|
historyMap[ historyId ] = pConstructFunction; |
|
} |
|
|
|
template< class DefaultState > |
|
void construct_with_history_impl( |
|
history_map_type & historyMap, |
|
const typename DefaultState::context_ptr_type & pContext ) |
|
{ |
|
typename history_map_type::iterator pFoundSlot = historyMap.find( |
|
history_key_type::make_history_key< DefaultState >() ); |
|
|
|
if ( ( pFoundSlot == historyMap.end() ) || ( pFoundSlot->second == 0 ) ) |
|
{ |
|
// We have never entered this state before or history was cleared |
|
DefaultState::deep_construct( |
|
pContext, *polymorphic_downcast< MostDerived * >( this ) ); |
|
} |
|
else |
|
{ |
|
typedef void construct_function( |
|
const typename DefaultState::context_ptr_type &, |
|
typename DefaultState::outermost_context_base_type & ); |
|
// 5.2.10.6 declares that reinterpret_casting a function pointer to a |
|
// different function pointer and back must yield the same value. The |
|
// following reinterpret_cast is the second half of such a sequence. |
|
construct_function * const pConstructFunction = |
|
reinterpret_cast< construct_function * >( pFoundSlot->second ); |
|
(*pConstructFunction)( |
|
pContext, *polymorphic_downcast< MostDerived * >( this ) ); |
|
} |
|
} |
|
|
|
typedef std::list< |
|
event_base_ptr_type, |
|
typename boost::detail::allocator::rebind_to< |
|
allocator_type, event_base_ptr_type >::type |
|
> event_queue_type; |
|
|
|
typedef std::map< |
|
const state_base_type *, event_queue_type, |
|
std::less< const state_base_type * >, |
|
typename boost::detail::allocator::rebind_to< |
|
allocator_type, |
|
std::pair< const state_base_type * const, event_queue_type > |
|
>::type |
|
> deferred_map_type; |
|
|
|
|
|
event_queue_type eventQueue_; |
|
event_queue_type deferredEventQueue_; |
|
state_list_type currentStates_; |
|
typename state_list_type::iterator currentStatesEnd_; |
|
state_base_type * pOutermostState_; |
|
bool isInnermostCommonOuter_; |
|
node_state_base_ptr_type pOutermostUnstableState_; |
|
ExceptionTranslator translator_; |
|
bool performFullExit_; |
|
history_map_type shallowHistoryMap_; |
|
history_map_type deepHistoryMap_; |
|
const event_base_type * pTriggeringEvent_; |
|
}; |
|
|
|
|
|
|
|
} // namespace statechart |
|
} // namespace boost |
|
|
|
|
|
|
|
#endif
|
|
|