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