mirror of
https://github.com/boostorg/msm.git
synced 2026-01-19 04:22:11 +00:00
refactor(backmp11): various topics
This commit is contained in:
committed by
Christian Granzin
parent
155b3ef6d6
commit
128228e667
@@ -21,9 +21,9 @@ It offers a significant reduction in compilation time and RAM usage, as can be s
|
||||
| back | 18 | 953 | 7
|
||||
| back_favor_compile_time | 21 | 1000 | 8
|
||||
| back11 | 43 | 2794 | 7
|
||||
| backmp11 | 3 | 229 | 3
|
||||
| backmp11_favor_compile_time | 3 | 220 | 8
|
||||
| sml | 12 | 363 | 3
|
||||
| backmp11 | 3 | 213 | 3
|
||||
| backmp11_favor_compile_time | 3 | 204 | 8
|
||||
| sml | 11 | 362 | 3
|
||||
|=======================================================================
|
||||
|
||||
|
||||
@@ -34,10 +34,10 @@ It offers a significant reduction in compilation time and RAM usage, as can be s
|
||||
| | Compile / sec | RAM / MB | Runtime / sec
|
||||
| back | 68 | 2849 | 23
|
||||
| back_favor_compile_time | 80 | 2551 | 261
|
||||
| backmp11 | 10 | 438 | 11
|
||||
| backmp11_favor_compile_time | 7 | 288 | 26
|
||||
| backmp11_favor_compile_time_multi_cu | 5 | ~919 | 26
|
||||
| sml | 48 | 1128 | 11
|
||||
| backmp11 | 9 | 381 | 11
|
||||
| backmp11_favor_compile_time | 7 | 285 | 26
|
||||
| backmp11_favor_compile_time_multi_cu | 5 | ~915 | 26
|
||||
| sml | 40 | 1056 | 11
|
||||
|================================================================================
|
||||
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
#include <boost/msm/back/common_types.hpp>
|
||||
#include <cstddef>
|
||||
|
||||
namespace boost { namespace msm { namespace backmp11
|
||||
namespace boost::msm::backmp11
|
||||
{
|
||||
|
||||
using process_result = back::HandledEnum;
|
||||
@@ -85,7 +85,6 @@ class deferred_event
|
||||
};
|
||||
|
||||
using EventSource = back::EventSourceEnum;
|
||||
using back::HandledEnum;
|
||||
|
||||
constexpr EventSource operator|(EventSource lhs, EventSource rhs)
|
||||
{
|
||||
@@ -146,7 +145,44 @@ class basic_unique_ptr
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace boost::msm::backmp11
|
||||
|
||||
}}} // namespace boost::msm::backmp11
|
||||
namespace boost::msm::back
|
||||
{
|
||||
|
||||
// Bitwise operations for process_result.
|
||||
// Defined in this header instead of back because type_traits are C++11.
|
||||
// Defined in the back namespace because the operations have to be in the
|
||||
// same namespace as HandledEnum.
|
||||
|
||||
constexpr HandledEnum operator|(HandledEnum lhs, HandledEnum rhs)
|
||||
{
|
||||
return static_cast<HandledEnum>(
|
||||
static_cast<std::underlying_type_t<HandledEnum>>(lhs) |
|
||||
static_cast<std::underlying_type_t<HandledEnum>>(rhs)
|
||||
);
|
||||
}
|
||||
|
||||
constexpr HandledEnum& operator|=(HandledEnum& lhs, HandledEnum rhs)
|
||||
{
|
||||
lhs = lhs | rhs;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
constexpr HandledEnum operator&(HandledEnum lhs, HandledEnum rhs)
|
||||
{
|
||||
return static_cast<HandledEnum>(
|
||||
static_cast<std::underlying_type_t<HandledEnum>>(lhs) &
|
||||
static_cast<std::underlying_type_t<HandledEnum>>(rhs)
|
||||
);
|
||||
}
|
||||
|
||||
constexpr HandledEnum& operator&=(HandledEnum& lhs, HandledEnum rhs)
|
||||
{
|
||||
lhs = lhs & rhs;
|
||||
return lhs;
|
||||
}
|
||||
|
||||
} // namespace boost::msm::back
|
||||
|
||||
#endif // BOOST_MSM_BACKMP11_COMMON_TYPES_H
|
||||
|
||||
@@ -16,19 +16,6 @@
|
||||
#include <boost/msm/backmp11/detail/dispatch_table.hpp>
|
||||
#include <boost/msm/backmp11/event_traits.hpp>
|
||||
|
||||
#include <boost/mpl/advance.hpp>
|
||||
#include <boost/mpl/begin.hpp>
|
||||
#include <boost/mpl/bool.hpp>
|
||||
#include <boost/mpl/empty.hpp>
|
||||
#include <boost/mpl/erase.hpp>
|
||||
#include <boost/mpl/eval_if.hpp>
|
||||
#include <boost/mpl/front.hpp>
|
||||
#include <boost/mpl/identity.hpp>
|
||||
#include <boost/mpl/int.hpp>
|
||||
#include <boost/mpl/pair.hpp>
|
||||
#include <boost/mpl/pop_front.hpp>
|
||||
|
||||
|
||||
namespace boost { namespace msm { namespace backmp11
|
||||
{
|
||||
|
||||
@@ -44,6 +31,10 @@ struct compile_policy_impl<favor_runtime_speed>
|
||||
{
|
||||
using add_forwarding_rows = mp11::mp_true;
|
||||
|
||||
// Bitmask for process result checks.
|
||||
static constexpr process_result handled_or_deferred =
|
||||
process_result::HANDLED_TRUE | process_result::HANDLED_DEFERRED;
|
||||
|
||||
template <typename Event>
|
||||
static constexpr bool is_completion_event(const Event&)
|
||||
{
|
||||
@@ -56,10 +47,10 @@ struct compile_policy_impl<favor_runtime_speed>
|
||||
return sm.template is_flag_active<EndInterruptFlag<Event>>();
|
||||
}
|
||||
|
||||
template <typename StateMachine, typename Event>
|
||||
static process_result process_event_internal(StateMachine& sm, const Event& event, EventSource source)
|
||||
template <typename Event>
|
||||
constexpr static const Event& normalize_event(const Event& event)
|
||||
{
|
||||
return sm.process_event_internal_impl(event, source);
|
||||
return event;
|
||||
}
|
||||
|
||||
template <typename StateMachine, typename Event>
|
||||
@@ -73,8 +64,7 @@ struct compile_policy_impl<favor_runtime_speed>
|
||||
|
||||
process_result process() override
|
||||
{
|
||||
return process_event_internal(
|
||||
m_sm,
|
||||
return m_sm.process_event_internal(
|
||||
m_event,
|
||||
EventSource::EVENT_SOURCE_DEFERRED);
|
||||
}
|
||||
@@ -89,11 +79,8 @@ struct compile_policy_impl<favor_runtime_speed>
|
||||
Event m_event;
|
||||
};
|
||||
|
||||
template <class State, class Event>
|
||||
using has_deferred_event = mp11::mp_contains<
|
||||
to_mp_list_t<typename State::deferred_events>,
|
||||
Event
|
||||
>;
|
||||
template <typename StateOrStates, typename Event>
|
||||
using has_deferred_event = has_deferred_event<StateOrStates, Event>;
|
||||
|
||||
template <typename StateMachine, typename Event>
|
||||
class is_event_deferred_helper
|
||||
@@ -106,14 +93,17 @@ struct compile_policy_impl<favor_runtime_speed>
|
||||
{
|
||||
result = true;
|
||||
};
|
||||
sm.template visit_if<defers_event,
|
||||
visit_mode::active_non_recursive>(visitor);
|
||||
// Apply a pre-filter with the 'has_deferred_events' predicate,
|
||||
// since this subset needs to be instantiated only once for the SM.
|
||||
sm.template visit_if<visit_mode::active_non_recursive,
|
||||
has_deferred_events, has_deferred_event>(visitor);
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
using deferring_states = typename StateMachine::deferring_states;
|
||||
template <typename State>
|
||||
using defers_event = has_deferred_event<State, Event>;
|
||||
using has_deferred_event = has_deferred_event<State, Event>;
|
||||
};
|
||||
|
||||
template <typename StateMachine, typename Event>
|
||||
@@ -128,9 +118,9 @@ struct compile_policy_impl<favor_runtime_speed>
|
||||
{
|
||||
if constexpr (is_kleene_event<Event>::value)
|
||||
{
|
||||
using event_list = typename StateMachine::event_set_mp11;
|
||||
typedef typename generate_event_set<typename StateMachine::internal::stt>::event_set_mp11 event_list;
|
||||
bool found =
|
||||
for_each_until<mp11::mp_transform<mp11::mp_identity, event_list>>(
|
||||
mp_for_each_until<mp11::mp_transform<mp11::mp_identity, event_list>>(
|
||||
[&sm, &event](auto event_identity)
|
||||
{
|
||||
using KnownEvent = typename decltype(event_identity)::type;
|
||||
@@ -177,49 +167,53 @@ struct compile_policy_impl<favor_runtime_speed>
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Fsm, typename State>
|
||||
struct table_index
|
||||
template<typename StateMachine, typename State, bool StateIsStateMachine = std::is_same_v<StateMachine, State>>
|
||||
struct get_table_index_impl;
|
||||
template<typename StateMachine, typename State>
|
||||
struct get_table_index_impl<StateMachine, State, false>
|
||||
{
|
||||
using type = mp11::mp_if<
|
||||
mp11::mp_not<is_same<State, Fsm>>,
|
||||
mp11::mp_size_t<Fsm::template get_state_id<State>() + 1>,
|
||||
mp11::mp_size_t<0>
|
||||
>;
|
||||
using type = mp11::mp_size_t<StateMachine::template get_state_id<State>() + 1>;
|
||||
};
|
||||
template<typename Fsm, typename State>
|
||||
using get_table_index = typename table_index<Fsm, State>::type;
|
||||
template<typename StateMachine, typename State>
|
||||
struct get_table_index_impl<StateMachine, State, true>
|
||||
{
|
||||
using type = mp11::mp_size_t<0>;
|
||||
};
|
||||
template<typename StateMachine, typename State>
|
||||
using get_table_index = typename get_table_index_impl<StateMachine, State>::type;
|
||||
|
||||
// Generates a singleton runtime lookup table that maps current state
|
||||
// to a function that makes the SM take its transition on the given
|
||||
// Event type.
|
||||
template<class Fsm>
|
||||
template<class StateMachine>
|
||||
class dispatch_table
|
||||
{
|
||||
using Stt = typename Fsm::complete_table;
|
||||
using Stt = typename StateMachine::complete_table;
|
||||
public:
|
||||
// Dispatch function for a specific event.
|
||||
template<class Event>
|
||||
using cell = HandledEnum (*)(Fsm&, int,int,Event const&);
|
||||
using cell = process_result (*)(StateMachine&, int&, Event const&);
|
||||
|
||||
// Dispatch an event.
|
||||
template<class Event>
|
||||
static HandledEnum dispatch(Fsm& fsm, int region_id, int state_id, const Event& event)
|
||||
static process_result dispatch(StateMachine& sm, int& state_id, const Event& event)
|
||||
{
|
||||
return event_dispatch_table<Event>::instance().entries[state_id+1](fsm, region_id, state_id, event);
|
||||
return event_dispatch_table<Event>::instance().entries[state_id+1](sm, state_id, event);
|
||||
}
|
||||
|
||||
// Dispatch an event to the FSM's internal table.
|
||||
template<class Event>
|
||||
static HandledEnum dispatch_internal(Fsm& fsm, int region_id, int state_id, const Event& event)
|
||||
static process_result dispatch_internal(StateMachine& sm, const Event& event)
|
||||
{
|
||||
return event_dispatch_table<Event>::instance().entries[0](fsm, region_id, state_id, event);
|
||||
int no_state_id;
|
||||
return event_dispatch_table<Event>::instance().entries[0](sm, no_state_id, event);
|
||||
}
|
||||
|
||||
private:
|
||||
// Compute the maximum state value in the sm so we know how big
|
||||
// to make the tables
|
||||
typedef typename generate_state_set<Stt>::state_set state_set;
|
||||
BOOST_STATIC_CONSTANT(int, max_state = (mp11::mp_size<state_set>::value));
|
||||
// to make the tables.
|
||||
using state_set = typename StateMachine::internal::state_set;
|
||||
static constexpr int max_state = mp11::mp_size<state_set>::value;
|
||||
|
||||
// Dispatch table for a specific event.
|
||||
template<class Event>
|
||||
@@ -235,39 +229,12 @@ struct compile_policy_impl<favor_runtime_speed>
|
||||
}
|
||||
|
||||
private:
|
||||
// A function object for use with mp11::mp_for_each that stuffs transitions into cells.
|
||||
class row_init_helper
|
||||
{
|
||||
public:
|
||||
row_init_helper(event_cell* entries)
|
||||
: m_entries(entries) {}
|
||||
|
||||
template<typename Row>
|
||||
typename ::boost::disable_if<typename is_kleene_event<typename Row::transition_event>::type, void>::type
|
||||
operator()(Row)
|
||||
{
|
||||
m_entries[get_table_index<Fsm, typename Row::current_state_type>::value] =
|
||||
&Row::execute;
|
||||
}
|
||||
|
||||
template<typename Row>
|
||||
typename ::boost::enable_if<typename is_kleene_event<typename Row::transition_event>::type, void>::type
|
||||
operator()(Row)
|
||||
{
|
||||
m_entries[get_table_index<Fsm, typename Row::current_state_type>::value] =
|
||||
&convert_event_and_forward<Row>::execute;
|
||||
}
|
||||
|
||||
private:
|
||||
event_cell* m_entries;
|
||||
};
|
||||
|
||||
static process_result execute_no_transition(Fsm&, int, int, const Event&)
|
||||
static process_result execute_no_transition(StateMachine&, int&, const Event&)
|
||||
{
|
||||
return process_result::HANDLED_FALSE;
|
||||
}
|
||||
|
||||
// initialize the dispatch table for a given Event and Fsm
|
||||
// Initialize the dispatch table for the event
|
||||
event_dispatch_table()
|
||||
{
|
||||
// Initialize cells for no transition
|
||||
@@ -289,15 +256,13 @@ struct compile_policy_impl<favor_runtime_speed>
|
||||
> map_of_row_seq;
|
||||
// and then build chaining rows for all source states having more than 1 row
|
||||
typedef mp11::mp_transform<
|
||||
row_chainer,
|
||||
transition_chainer,
|
||||
map_of_row_seq
|
||||
> chained_rows;
|
||||
|
||||
// Go back and fill in cells for matching transitions.
|
||||
// MSVC crashes when using get_init_cells.
|
||||
#if !defined(_MSC_VER)
|
||||
typedef mp11::mp_transform<
|
||||
preprocess_row,
|
||||
preprocess_transition,
|
||||
chained_rows
|
||||
> chained_and_preprocessed_rows;
|
||||
event_cell_initializer::init(
|
||||
@@ -305,121 +270,67 @@ struct compile_policy_impl<favor_runtime_speed>
|
||||
get_init_cells<event_cell, chained_and_preprocessed_rows>(),
|
||||
mp11::mp_size<chained_and_preprocessed_rows>::value
|
||||
);
|
||||
#else
|
||||
mp11::mp_for_each<chained_rows>(row_init_helper{entries});
|
||||
#endif
|
||||
}
|
||||
|
||||
// class used to build a chain (or sequence) of transitions for a given event and start state
|
||||
// (like an UML diamond). Allows transition conflicts.
|
||||
template< typename Seq,typename AnEvent,typename State >
|
||||
struct chain_row
|
||||
// Class used to build a chain of transitions for a given event and state.
|
||||
// Allows transition conflicts.
|
||||
template<typename State, typename Transitions>
|
||||
struct transition_chain
|
||||
{
|
||||
typedef State current_state_type;
|
||||
typedef AnEvent transition_event;
|
||||
using current_state_type = State;
|
||||
using transition_event = Event;
|
||||
|
||||
// helper for building a disable/enable_if-controlled execute function
|
||||
struct execute_helper
|
||||
static process_result execute(StateMachine& sm, int& state_id, Event const& evt)
|
||||
{
|
||||
template <class Sequence>
|
||||
static
|
||||
HandledEnum
|
||||
execute(Fsm& , int, int, Event const& , ::boost::mpl::true_ const & )
|
||||
process_result result = process_result::HANDLED_FALSE;
|
||||
mp_for_each_until<Transitions>(
|
||||
[&result, &sm, &state_id, &evt](auto transition)
|
||||
{
|
||||
// if at least one guard rejected, this will be ignored, otherwise will generate an error
|
||||
return HandledEnum::HANDLED_FALSE;
|
||||
using Transition = decltype(transition);
|
||||
result |= Transition::execute(sm, state_id, evt);
|
||||
if (result & handled_or_deferred)
|
||||
{
|
||||
// If a guard rejected previously, ensure this bit is not present.
|
||||
result &= handled_or_deferred;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class Sequence>
|
||||
static
|
||||
HandledEnum
|
||||
execute(Fsm& fsm, int region_index , int state, Event const& evt,
|
||||
::boost::mpl::false_ const & )
|
||||
{
|
||||
// try the first guard
|
||||
typedef typename ::boost::mpl::front<Sequence>::type first_row;
|
||||
HandledEnum res = first_row::execute(fsm,region_index,state,evt);
|
||||
if (HandledEnum::HANDLED_TRUE!=res && HandledEnum::HANDLED_DEFERRED!=res)
|
||||
{
|
||||
// if the first rejected, move on to the next one
|
||||
HandledEnum sub_res =
|
||||
execute<typename ::boost::mpl::pop_front<Sequence>::type>(fsm,region_index,state,evt,
|
||||
::boost::mpl::bool_<
|
||||
::boost::mpl::empty<typename ::boost::mpl::pop_front<Sequence>::type>::type::value>());
|
||||
// if at least one guards rejects, the event will not generate a call to no_transition
|
||||
if ((HandledEnum::HANDLED_FALSE==sub_res) && (HandledEnum::HANDLED_GUARD_REJECT==res) )
|
||||
return HandledEnum::HANDLED_GUARD_REJECT;
|
||||
else
|
||||
return sub_res;
|
||||
return false;
|
||||
}
|
||||
return res;
|
||||
);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
// Take the transition action and return the next state.
|
||||
static HandledEnum execute(Fsm& fsm, int region_index, int state, Event const& evt)
|
||||
|
||||
template <
|
||||
typename State,
|
||||
typename FilteredTransitionTable,
|
||||
bool MoreThanOneFrow = (mp11::mp_count_if<FilteredTransitionTable, has_is_frow>::value > 1)>
|
||||
struct make_transition_chain_impl;
|
||||
template <typename State, typename FilteredTransitionTable>
|
||||
struct make_transition_chain_impl<State, FilteredTransitionTable, false>
|
||||
{
|
||||
// forward to helper
|
||||
return execute_helper::template execute<Seq>(fsm,region_index,state,evt,
|
||||
::boost::mpl::bool_< ::boost::mpl::empty<Seq>::type::value>());
|
||||
}
|
||||
using type = transition_chain<State, FilteredTransitionTable>;
|
||||
};
|
||||
// nullary metafunction whose only job is to prevent early evaluation of _1
|
||||
template< typename Entry >
|
||||
struct make_chain_row_from_map_entry
|
||||
template <typename State, typename FilteredTransitionTable>
|
||||
struct make_transition_chain_impl<State, FilteredTransitionTable, true>
|
||||
{
|
||||
// if we have more than one frow with the same state as source, remove the ones extra
|
||||
// note: we know the frow's are located at the beginning so we remove at the beginning (number of frows - 1) elements
|
||||
enum { number_frows = boost::mp11::mp_count_if<typename Entry::second, has_is_frow>::value };
|
||||
// note: we know the frows are located at the beginning so we remove at the beginning
|
||||
// (number of frows - 1) elements
|
||||
static constexpr size_t number_frows =
|
||||
boost::mp11::mp_count_if<FilteredTransitionTable, has_is_frow>::value;
|
||||
using type =
|
||||
transition_chain<State, mp11::mp_drop_c<FilteredTransitionTable, number_frows - 1>>;
|
||||
};
|
||||
template <typename State, typename FilteredTransitionTable>
|
||||
using make_transition_chain = typename make_transition_chain_impl<State, FilteredTransitionTable>::type;
|
||||
|
||||
//erases the first NumberToDelete rows
|
||||
template<class Sequence, int NumberToDelete>
|
||||
struct erase_first_rows
|
||||
template <typename Transition>
|
||||
static process_result convert_event_and_execute(StateMachine& sm, int& state_id, Event const& evt)
|
||||
{
|
||||
typedef typename ::boost::mpl::erase<
|
||||
typename Entry::second,
|
||||
typename ::boost::mpl::begin<Sequence>::type,
|
||||
typename ::boost::mpl::advance<
|
||||
typename ::boost::mpl::begin<Sequence>::type,
|
||||
::boost::mpl::int_<NumberToDelete> >::type
|
||||
>::type type;
|
||||
};
|
||||
// if we have more than 1 frow with this event (not allowed), delete the spare
|
||||
typedef typename ::boost::mpl::eval_if<
|
||||
typename ::boost::mpl::bool_< number_frows >= 2 >::type,
|
||||
erase_first_rows<typename Entry::second,number_frows-1>,
|
||||
::boost::mpl::identity<typename Entry::second>
|
||||
>::type filtered_stt;
|
||||
|
||||
typedef chain_row<filtered_stt,Event,
|
||||
typename Entry::first > type;
|
||||
};
|
||||
// helper for lazy evaluation in eval_if of change_frow_event
|
||||
template <class Transition,class NewEvent>
|
||||
struct replace_event
|
||||
{
|
||||
typedef typename Transition::template replace_event<NewEvent>::type type;
|
||||
};
|
||||
// changes the event type for a frow to the event we are dispatching
|
||||
// this helps ensure that an event does not get processed more than once because of frows and base events.
|
||||
template <class FrowTransition>
|
||||
struct change_frow_event
|
||||
{
|
||||
typedef typename ::boost::mp11::mp_if_c<
|
||||
has_is_frow<FrowTransition>::type::value,
|
||||
replace_event<FrowTransition,Event>,
|
||||
boost::mp11::mp_identity<FrowTransition>
|
||||
>::type type;
|
||||
};
|
||||
|
||||
template <class Row>
|
||||
struct convert_event_and_forward
|
||||
{
|
||||
static HandledEnum execute(Fsm& fsm, int region_index, int state, Event const& evt)
|
||||
{
|
||||
typename Row::transition_event forwarded(evt);
|
||||
return Row::execute(fsm,region_index,state,forwarded);
|
||||
typename Transition::transition_event kleene_event{evt};
|
||||
return Transition::execute(sm, state_id, kleene_event);
|
||||
}
|
||||
};
|
||||
|
||||
using event_init_cell_value = init_cell_value<event_cell>;
|
||||
|
||||
@@ -433,69 +344,124 @@ struct compile_policy_impl<favor_runtime_speed>
|
||||
|
||||
// Helpers for row processing
|
||||
// First operation (fold)
|
||||
template <typename T, bool IsKleeneEvent = is_kleene_event<typename T::transition_event>::value>
|
||||
struct event_filter_predicate_impl
|
||||
{
|
||||
using type = std::is_base_of<typename T::transition_event, Event>;
|
||||
};
|
||||
template <typename T>
|
||||
using event_filter_predicate = mp11::mp_and<
|
||||
mp11::mp_not<has_not_real_row_tag<T>>,
|
||||
mp11::mp_or<
|
||||
std::is_base_of<typename T::transition_event, Event>,
|
||||
typename is_kleene_event<typename T::transition_event>::type
|
||||
>
|
||||
>;
|
||||
struct event_filter_predicate_impl<T, true>
|
||||
{
|
||||
using type = mp11::mp_true;
|
||||
};
|
||||
template <typename T>
|
||||
using event_filter_predicate =
|
||||
typename event_filter_predicate_impl<T>::type;
|
||||
|
||||
// Changes the event type for a frow to the event we are dispatching.
|
||||
// This helps ensure that an event does not get processed more than once
|
||||
// because of frows and base events.
|
||||
template <typename Transition, bool IsFrow = has_is_frow<Transition>::value>
|
||||
struct normalize_transition_impl;
|
||||
template <typename Transition>
|
||||
struct normalize_transition_impl<Transition, false>
|
||||
{
|
||||
using type = Transition;
|
||||
};
|
||||
template <typename Transition>
|
||||
struct normalize_transition_impl<Transition, true>
|
||||
{
|
||||
using type = typename Transition::template replace_event<Event>;
|
||||
};
|
||||
template <typename Transition>
|
||||
using normalize_transition = typename normalize_transition_impl<Transition>::type;
|
||||
|
||||
template <typename M, typename Key, typename Value>
|
||||
using push_map_value = mp11::mp_push_front<
|
||||
mp11::mp_second<mp11::mp_map_find<M, Key>>,
|
||||
Value>;
|
||||
template<typename M, typename T>
|
||||
using map_updater = mp11::mp_map_replace<
|
||||
M,
|
||||
mp11::mp_list<
|
||||
typename T::current_state_type,
|
||||
mp11::mp_eval_if_c<
|
||||
!mp11::mp_map_contains<M, typename T::current_state_type>::value,
|
||||
// first row on this source state, make a list with 1 element
|
||||
mp11::mp_list<typename change_frow_event<T>::type>,
|
||||
template<typename Map, typename Transition, bool FirstEntry = !mp11::mp_map_contains<Map, typename Transition::current_state_type>::value>
|
||||
struct map_updater_impl;
|
||||
template<typename Map, typename Transition>
|
||||
struct map_updater_impl<Map, Transition, false>
|
||||
{
|
||||
using type = mp11::mp_map_replace<
|
||||
Map,
|
||||
// list already exists, add the row
|
||||
push_map_value,
|
||||
M,
|
||||
typename T::current_state_type,
|
||||
typename change_frow_event<T>::type
|
||||
mp11::mp_list<
|
||||
typename Transition::current_state_type,
|
||||
push_map_value<
|
||||
Map,
|
||||
typename Transition::current_state_type,
|
||||
normalize_transition<Transition>
|
||||
>
|
||||
>
|
||||
>;
|
||||
};
|
||||
template<typename Map, typename Transition>
|
||||
struct map_updater_impl<Map, Transition, true>
|
||||
{
|
||||
using type = mp11::mp_map_replace<
|
||||
Map,
|
||||
mp11::mp_list<
|
||||
typename Transition::current_state_type,
|
||||
// first row on this source state, make a list with 1 element
|
||||
mp11::mp_list<normalize_transition<Transition>>
|
||||
>
|
||||
>;
|
||||
};
|
||||
template<typename Map, typename Transition>
|
||||
using map_updater = typename map_updater_impl<Map, Transition>::type;
|
||||
|
||||
// Second operation (transform)
|
||||
template<typename T>
|
||||
using to_mpl_map_entry = mpl::pair<
|
||||
mp11::mp_first<T>,
|
||||
mp11::mp_second<T>
|
||||
>;
|
||||
template<typename T>
|
||||
using row_chainer = mp11::mp_if_c<
|
||||
(mp11::mp_size<to_mp_list_t<mp11::mp_second<T>>>::value > 1),
|
||||
// we need row chaining
|
||||
typename make_chain_row_from_map_entry<to_mpl_map_entry<T>>::type,
|
||||
template<
|
||||
typename StateAndFilteredTransitionTable,
|
||||
bool MultipleTransitions = (mp11::mp_size<mp11::mp_second<StateAndFilteredTransitionTable>>::value > 1)>
|
||||
struct transition_chainer_impl;
|
||||
template<typename StateAndFilteredTransitionTable>
|
||||
struct transition_chainer_impl<StateAndFilteredTransitionTable, false>
|
||||
{
|
||||
// just one row, no chaining, we rebuild the row like it was before
|
||||
mp11::mp_front<mp11::mp_second<T>>
|
||||
>;
|
||||
template<typename Row>
|
||||
using preprocess_row_helper = cell_constant<&Row::execute>;
|
||||
template<typename Row>
|
||||
using preprocess_row = init_cell_constant<
|
||||
using type = mp11::mp_front<mp11::mp_second<StateAndFilteredTransitionTable>>;
|
||||
};
|
||||
template<typename StateAndFilteredTransitionTable>
|
||||
struct transition_chainer_impl<StateAndFilteredTransitionTable, true>
|
||||
{
|
||||
// we need row chaining
|
||||
using type = make_transition_chain<
|
||||
mp11::mp_first<StateAndFilteredTransitionTable>,
|
||||
mp11::mp_second<StateAndFilteredTransitionTable>>;
|
||||
};
|
||||
template<typename StateAndFilteredTransitionTable>
|
||||
using transition_chainer = typename transition_chainer_impl<StateAndFilteredTransitionTable>::type;
|
||||
template<typename Transition, bool IsKleeneEvent = is_kleene_event<typename Transition::transition_event>::value>
|
||||
struct preprocess_transition_impl;
|
||||
template<typename Transition>
|
||||
struct preprocess_transition_impl<Transition, false>
|
||||
{
|
||||
using type = init_cell_constant<
|
||||
// Offset into the entries array
|
||||
get_table_index<Fsm, typename Row::current_state_type>::value,
|
||||
get_table_index<StateMachine, typename Transition::current_state_type>::value,
|
||||
// Address of the execute function
|
||||
mp11::mp_eval_if_c<
|
||||
is_kleene_event<typename Row::transition_event>::type::value,
|
||||
cell_constant<
|
||||
&convert_event_and_forward<Row>::execute
|
||||
>,
|
||||
preprocess_row_helper,
|
||||
Row
|
||||
>::value
|
||||
cell_constant<&Transition::execute>::value
|
||||
>;
|
||||
};
|
||||
template<typename Transition>
|
||||
struct preprocess_transition_impl<Transition, true>
|
||||
{
|
||||
using type = init_cell_constant<
|
||||
// Offset into the entries array
|
||||
get_table_index<StateMachine, typename Transition::current_state_type>::value,
|
||||
// Address of the execute function
|
||||
cell_constant<&convert_event_and_execute<Transition>>::value
|
||||
>;
|
||||
};
|
||||
template<typename Transition>
|
||||
using preprocess_transition = typename preprocess_transition_impl<Transition>::type;
|
||||
|
||||
// data members
|
||||
public:
|
||||
// max_state+1, because 0 is reserved for this fsm (internal transitions)
|
||||
// max_state+1, because 0 is reserved for this sm (internal transitions)
|
||||
event_cell entries[max_state+1];
|
||||
};
|
||||
};
|
||||
|
||||
@@ -17,17 +17,13 @@
|
||||
|
||||
#include <boost/mpl/copy.hpp>
|
||||
#include <boost/mpl/is_sequence.hpp>
|
||||
#include <boost/mpl/front.hpp>
|
||||
|
||||
#include <boost/type_traits/is_same.hpp>
|
||||
#include <boost/utility/enable_if.hpp>
|
||||
|
||||
#include <boost/msm/row_tags.hpp>
|
||||
|
||||
#include <boost/msm/backmp11/common_types.hpp>
|
||||
#include <boost/msm/backmp11/detail/state_tags.hpp>
|
||||
#include <boost/msm/back/traits.hpp>
|
||||
#include <boost/msm/front/detail/common_states.hpp>
|
||||
|
||||
#include <boost/msm/front/detail/state_tags.hpp>
|
||||
#include <boost/msm/front/completion_event.hpp>
|
||||
#include <boost/msm/row_tags.hpp>
|
||||
|
||||
namespace boost { namespace msm { namespace backmp11
|
||||
{
|
||||
@@ -35,44 +31,25 @@ namespace boost { namespace msm { namespace backmp11
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template <typename>
|
||||
using always_true = mp11::mp_true;
|
||||
|
||||
constexpr bool has_flag(visit_mode value, visit_mode flag)
|
||||
{
|
||||
return (static_cast<int>(value) & static_cast<int>(flag)) != 0;
|
||||
}
|
||||
|
||||
struct back_end_tag {};
|
||||
|
||||
template <typename T>
|
||||
using has_back_end_tag = std::is_same<typename T::internal::tag, back_end_tag>;
|
||||
|
||||
template <class T>
|
||||
using is_back_end = has_back_end_tag<T>;
|
||||
|
||||
template <typename T>
|
||||
using is_composite = mp11::mp_or<
|
||||
std::is_same<typename T::internal::tag, msm::front::detail::composite_state_tag>,
|
||||
has_back_end_tag<T>
|
||||
>;
|
||||
|
||||
// Call a functor on all elements of List, until the functor returns true.
|
||||
template <typename List, typename Func>
|
||||
constexpr bool for_each_until(Func&& func)
|
||||
template <typename... Ts>
|
||||
struct for_each_until_impl
|
||||
{
|
||||
bool condition = false;
|
||||
|
||||
boost::mp11::mp_for_each<List>(
|
||||
[&func, &condition](auto&& item)
|
||||
template <typename F>
|
||||
static constexpr bool invoke(F &&func)
|
||||
{
|
||||
if (!condition)
|
||||
{
|
||||
condition = func(std::forward<decltype(item)>(item));
|
||||
return (... || func(Ts{}));
|
||||
}
|
||||
}
|
||||
);
|
||||
return condition;
|
||||
};
|
||||
template <typename L, typename F>
|
||||
constexpr bool mp_for_each_until(F &&func)
|
||||
{
|
||||
return mp11::mp_apply<for_each_until_impl, L>::invoke(std::forward<F>(func));
|
||||
}
|
||||
|
||||
// Wrapper for an instance of a type, which might not be present.
|
||||
@@ -111,29 +88,165 @@ struct to_mp_list<mp11::mp_list<T...>>
|
||||
template<typename ...T>
|
||||
using to_mp_list_t = typename to_mp_list<T...>::type;
|
||||
|
||||
template <class stt>
|
||||
struct generate_state_set;
|
||||
|
||||
// iterates through a transition table to generate an ordered state set
|
||||
// first the source states, transition up to down
|
||||
// then the target states, up to down
|
||||
template <class Stt>
|
||||
struct generate_state_set
|
||||
// Helper to convert a front-end state to a back-end state.
|
||||
template <typename StateMachine, typename State, typename Enable = void>
|
||||
struct convert_state_impl
|
||||
{
|
||||
typedef to_mp_list_t<Stt> stt;
|
||||
// first add the source states
|
||||
template <typename V, typename T>
|
||||
using set_push_source_state =
|
||||
mp11::mp_set_push_back<V, typename T::current_state_type>;
|
||||
using source_state_set =
|
||||
mp11::mp_fold<stt, mp11::mp_list<>, set_push_source_state>;
|
||||
// then add the target states
|
||||
template <typename V, typename T>
|
||||
using set_push_target_state =
|
||||
mp11::mp_set_push_back<V, typename T::next_state_type>;
|
||||
using state_set =
|
||||
mp11::mp_fold<stt, source_state_set, set_push_target_state>;
|
||||
using type = State;
|
||||
};
|
||||
// Specialization for a 'direct' state wrapper struct used as target state (defined in the back-end).
|
||||
template <typename StateMachine, typename State>
|
||||
struct convert_state_impl<StateMachine, State, std::enable_if_t<has_explicit_entry_be_tag<State>::value>>
|
||||
{
|
||||
using type = typename State::owner;
|
||||
};
|
||||
// Specialization for a "direct fork", a sequence of 'direct' state wrappers used directly as the target state.
|
||||
template <typename StateMachine, typename State>
|
||||
struct convert_state_impl<StateMachine, State, std::enable_if_t<mpl::is_sequence<State>::value>>
|
||||
{
|
||||
using target_states = to_mp_list_t<State>;
|
||||
using type = typename mp11::mp_front<target_states>::owner;
|
||||
};
|
||||
// Specialization for a 'entry_pt' state wrapper struct (defined in the back-end).
|
||||
template <typename StateMachine, typename State>
|
||||
struct convert_state_impl<StateMachine, State, std::enable_if_t<has_entry_pseudostate_be_tag<State>::value>>
|
||||
{
|
||||
using type = typename State::owner;
|
||||
};
|
||||
// Specialization for an 'exit_pseudo_state' struct (defined in the front-end).
|
||||
// This converts the FE definition to a BE definition to establish the
|
||||
// connection to the target SM.
|
||||
template <typename StateMachine, typename State>
|
||||
struct convert_state_impl<StateMachine, State, std::enable_if_t<front::detail::has_exit_pseudostate_tag<State>::value>>
|
||||
{
|
||||
using type = typename StateMachine::template exit_pt<State>;
|
||||
};
|
||||
// Specialization for a 'exit_pt' struct (defined in the back-end).
|
||||
template <typename StateMachine, typename State>
|
||||
struct convert_state_impl<StateMachine, State, std::enable_if_t<has_exit_pseudostate_be_tag<State>::value>>
|
||||
{
|
||||
using type = typename State::owner;
|
||||
};
|
||||
|
||||
template <typename StateMachine, typename State, typename Enable = void>
|
||||
struct convert_source_state_impl : convert_state_impl<StateMachine, State> {};
|
||||
template <typename StateMachine, typename State>
|
||||
struct convert_source_state_impl<
|
||||
StateMachine,
|
||||
State,
|
||||
std::enable_if_t<front::detail::has_exit_pseudostate_tag<State>::value>>
|
||||
: convert_state_impl<StateMachine, State>
|
||||
{
|
||||
// An 'exit_pseudo_state' denotes the first target of a compound transition,
|
||||
// it must not be used as source state.
|
||||
static_assert(!front::detail::has_exit_pseudostate_tag<State>::value,
|
||||
"'exit_pseudo_state' is only allowed as target state");
|
||||
};
|
||||
template <typename StateMachine, typename State>
|
||||
struct convert_source_state_impl<
|
||||
StateMachine,
|
||||
State,
|
||||
std::enable_if_t<has_explicit_entry_be_tag<State>::value>>
|
||||
: convert_state_impl<StateMachine, State>
|
||||
{
|
||||
// Explicit entries can only denote targets.
|
||||
static_assert(!has_explicit_entry_be_tag<State>::value,
|
||||
"'direct' is only allowed as target state");
|
||||
};
|
||||
template <typename StateMachine, typename State>
|
||||
struct convert_source_state_impl<
|
||||
StateMachine,
|
||||
State,
|
||||
std::enable_if_t<mpl::is_sequence<State>::value>>
|
||||
: convert_state_impl<StateMachine, State>
|
||||
{
|
||||
// Explicit entries can only denote targets.
|
||||
static_assert(!mpl::is_sequence<State>::value,
|
||||
"'fork' is only allowed as target state");
|
||||
};
|
||||
template <typename StateMachine, typename State>
|
||||
using convert_source_state = typename convert_source_state_impl<StateMachine, State>::type;
|
||||
|
||||
template <typename StateMachine, typename State, typename Enable = void>
|
||||
struct convert_target_state_impl : convert_state_impl<StateMachine, State> {};
|
||||
template <typename StateMachine, typename State>
|
||||
struct convert_target_state_impl<
|
||||
StateMachine,
|
||||
State,
|
||||
std::enable_if_t<has_exit_pseudostate_be_tag<State>::value>>
|
||||
: convert_state_impl<StateMachine, State>
|
||||
{
|
||||
// An exit_pt denotes the second source of a compound transition,
|
||||
// it must not be used as target state.
|
||||
// This also ensures that this transition can only be executed as a result of the
|
||||
// predecessor transition (with the 'exit_pseudo_state' as target state)
|
||||
// having been executed.
|
||||
static_assert(!has_exit_pseudostate_be_tag<State>::value,
|
||||
"'exit_pt' is only allowed as source state");
|
||||
};
|
||||
template <typename StateMachine, typename State>
|
||||
struct convert_target_state_impl<
|
||||
StateMachine,
|
||||
State,
|
||||
std::enable_if_t<front::detail::has_entry_pseudostate_tag<State>::value>>
|
||||
: convert_state_impl<StateMachine, State>
|
||||
{
|
||||
static_assert(!front::detail::has_entry_pseudostate_tag<State>::value,
|
||||
"'entry_pseudo_state' is only allowed as source state");
|
||||
};
|
||||
template <typename StateMachine, typename State>
|
||||
using convert_target_state = typename convert_target_state_impl<StateMachine, State>::type;
|
||||
|
||||
// Parses a state machine to generate a state set.
|
||||
// The implementation in this metafunction defines the state id order:
|
||||
// - source states
|
||||
// - target states
|
||||
// - initial states
|
||||
// (if not already mentioned in the transition table)
|
||||
// - states in the explicit_creation property
|
||||
// (if not already mentioned in the transition table and the property exists)
|
||||
template <typename StateMachine>
|
||||
struct generate_state_set_impl
|
||||
{
|
||||
using front_end_t = typename StateMachine::front_end_t;
|
||||
using transition_table = to_mp_list_t<typename front_end_t::transition_table>;
|
||||
|
||||
// First add the source states.
|
||||
template <typename V, typename T>
|
||||
using set_push_source_state = mp11::mp_if_c<
|
||||
!std::is_same_v<typename T::Source, front::none>,
|
||||
mp11::mp_set_push_back<V, convert_source_state<StateMachine, typename T::Source>>,
|
||||
V>;
|
||||
using partial_state_set_0 =
|
||||
mp11::mp_fold<transition_table, mp11::mp_list<>, set_push_source_state>;
|
||||
|
||||
// Then add the target states.
|
||||
template <typename V, typename T>
|
||||
using set_push_target_state = mp11::mp_if_c<
|
||||
!std::is_same_v<typename T::Target, front::none>,
|
||||
mp11::mp_set_push_back<V, convert_target_state<StateMachine, typename T::Target>>,
|
||||
V>;
|
||||
using partial_state_set_1 =
|
||||
mp11::mp_fold<transition_table, partial_state_set_0, set_push_target_state>;
|
||||
|
||||
// Then add the initial states.
|
||||
using initial_states = to_mp_list_t<typename front_end_t::initial_state>;
|
||||
static_assert(mp11::mp_is_set<initial_states>::value, "Each initial state must be unique");
|
||||
using partial_state_set_2 = mp11::mp_set_union<partial_state_set_1, initial_states>;
|
||||
|
||||
// Then add the states marked for explicit creation.
|
||||
template <typename T>
|
||||
using add_explicit_creation_states =
|
||||
mp11::mp_set_union<partial_state_set_2, to_mp_list_t<typename T::explicit_creation>>;
|
||||
using type = mp11::mp_eval_if_c<
|
||||
!has_explicit_creation<front_end_t>::value,
|
||||
partial_state_set_2,
|
||||
add_explicit_creation_states,
|
||||
front_end_t
|
||||
>;
|
||||
};
|
||||
template <typename StateMachine>
|
||||
using generate_state_set = typename generate_state_set_impl<StateMachine>::type;
|
||||
|
||||
// extends a state set to a map with key=state and value=id
|
||||
template <class StateSet>
|
||||
@@ -201,148 +314,45 @@ struct get_event_id
|
||||
enum {value = type::value};
|
||||
};
|
||||
|
||||
template <class State>
|
||||
using has_deferred_events = mp11::mp_not<
|
||||
template <typename State>
|
||||
struct has_deferred_events_impl
|
||||
{
|
||||
using type = mp11::mp_not<
|
||||
mp11::mp_empty<to_mp_list_t<typename State::deferred_events>>
|
||||
>;
|
||||
};
|
||||
template <typename... States>
|
||||
struct has_deferred_events_impl<mp11::mp_list<States...>>
|
||||
{
|
||||
using states = mp11::mp_list<States...>;
|
||||
template <typename State>
|
||||
using has_deferred_events = typename has_deferred_events_impl<State>::type;
|
||||
|
||||
template <class State, class Event>
|
||||
using has_deferred_event = mp11::mp_contains<
|
||||
using type = mp11::mp_any_of<states, has_deferred_events>;
|
||||
};
|
||||
template <typename StateOrStates>
|
||||
using has_deferred_events = typename has_deferred_events_impl<StateOrStates>::type;
|
||||
|
||||
template <typename State, typename Event>
|
||||
struct has_deferred_event_impl
|
||||
{
|
||||
using type = mp11::mp_contains<
|
||||
to_mp_list_t<typename State::deferred_events>,
|
||||
Event
|
||||
>;
|
||||
|
||||
// Template used to create dummy entries for initial states not found in the stt.
|
||||
template< typename T1 >
|
||||
struct not_a_row
|
||||
{
|
||||
typedef int not_real_row_tag;
|
||||
struct dummy_event
|
||||
{
|
||||
};
|
||||
typedef T1 current_state_type;
|
||||
typedef T1 next_state_type;
|
||||
typedef dummy_event transition_event;
|
||||
};
|
||||
|
||||
// used for states created with explicit_creation
|
||||
// if the state is an explicit entry, we reach for the wrapped state
|
||||
// otherwise, this returns the state itself
|
||||
template <class State>
|
||||
struct get_wrapped_state
|
||||
template <typename... States, typename Event>
|
||||
struct has_deferred_event_impl<mp11::mp_list<States...>, Event>
|
||||
{
|
||||
template <typename T>
|
||||
using get_wrapped_entry = typename T::wrapped_entry;
|
||||
using type = mp11::mp_eval_or<State, get_wrapped_entry, State>;
|
||||
};
|
||||
|
||||
// returns the transition table of a Composite state
|
||||
template <class Derived>
|
||||
struct get_transition_table
|
||||
{
|
||||
typedef typename Derived::internal::template create_real_stt<typename Derived::front_end_t>::type Stt;
|
||||
// get the state set
|
||||
typedef typename generate_state_set<Stt>::state_set states;
|
||||
// iterate through the initial states and add them in the stt if not already there
|
||||
typedef typename Derived::internal::initial_states initial_states;
|
||||
template<typename V, typename T>
|
||||
using states_pusher = mp11::mp_if_c<
|
||||
mp11::mp_set_contains<states, T>::value,
|
||||
V,
|
||||
mp11::mp_push_back<
|
||||
V,
|
||||
not_a_row<typename get_wrapped_state<T>::type>
|
||||
>
|
||||
>;
|
||||
typedef typename mp11::mp_fold<
|
||||
to_mp_list_t<initial_states>,
|
||||
to_mp_list_t<Stt>,
|
||||
states_pusher
|
||||
> with_init;
|
||||
|
||||
// do the same for states marked as explicitly created
|
||||
template<typename T>
|
||||
using get_explicit_creation = to_mp_list_t<typename T::explicit_creation>;
|
||||
using fake_explicit_created = mp11::mp_eval_or<
|
||||
mp11::mp_list<>,
|
||||
get_explicit_creation,
|
||||
Derived
|
||||
>;
|
||||
//converts a "fake" (simulated in a state_machine_ description )state into one which will really get created
|
||||
template <class State>
|
||||
using convert_fake_state = mp11::mp_if_c<
|
||||
has_direct_entry<State>::value,
|
||||
typename Derived::template direct<State>,
|
||||
State
|
||||
>;
|
||||
using explicit_created = mp11::mp_transform<
|
||||
convert_fake_state,
|
||||
fake_explicit_created
|
||||
>;
|
||||
|
||||
typedef typename mp11::mp_fold<
|
||||
to_mp_list_t<explicit_created>,
|
||||
with_init,
|
||||
states_pusher
|
||||
> type;
|
||||
};
|
||||
template<typename T>
|
||||
using get_transition_table_t = typename get_transition_table<T>::type;
|
||||
|
||||
// recursively builds an internal table including those of substates, sub-substates etc.
|
||||
// variant for submachines
|
||||
template <class State, bool IsComposite>
|
||||
struct recursive_get_internal_transition_table
|
||||
{
|
||||
// get the composite's internal table
|
||||
typedef typename State::front_end_t::internal_transition_table composite_table;
|
||||
// and for every substate (state of submachine), recursively get the internal transition table
|
||||
using composite_states = typename State::internal::state_set;
|
||||
template<typename V, typename SubState>
|
||||
using append_recursive_internal_transition_table = mp11::mp_append<
|
||||
V,
|
||||
typename recursive_get_internal_transition_table<SubState, has_back_end_tag<SubState>::value>::type
|
||||
>;
|
||||
typedef typename mp11::mp_fold<
|
||||
composite_states,
|
||||
to_mp_list_t<composite_table>,
|
||||
append_recursive_internal_transition_table
|
||||
> type;
|
||||
};
|
||||
// stop iterating on leafs (simple states)
|
||||
template <class State>
|
||||
struct recursive_get_internal_transition_table<State, false>
|
||||
{
|
||||
typedef to_mp_list_t<
|
||||
typename State::internal_transition_table
|
||||
> type;
|
||||
};
|
||||
// recursively get a transition table for a given composite state.
|
||||
// returns the transition table for this state + the tables of all composite sub states recursively
|
||||
template <class Composite>
|
||||
struct recursive_get_transition_table
|
||||
{
|
||||
// get the transition table of the state if it's a state machine
|
||||
typedef typename mp11::mp_eval_if_c<
|
||||
!has_back_end_tag<Composite>::value,
|
||||
mp11::mp_list<>,
|
||||
get_transition_table_t,
|
||||
Composite
|
||||
> org_table;
|
||||
|
||||
typedef typename generate_state_set<org_table>::state_set states;
|
||||
|
||||
// and for every substate, recursively get the transition table if it's a state machine
|
||||
template<typename V, typename T>
|
||||
using append_recursive_transition_table = mp11::mp_append<
|
||||
V,
|
||||
typename recursive_get_transition_table<T>::type
|
||||
>;
|
||||
typedef typename mp11::mp_fold<
|
||||
states,
|
||||
org_table,
|
||||
append_recursive_transition_table> type;
|
||||
using states = mp11::mp_list<States...>;
|
||||
template <typename State>
|
||||
using has_deferred_event = typename has_deferred_event_impl<State, Event>::type;
|
||||
using subset = mp11::mp_copy_if<states, has_deferred_event>;
|
||||
|
||||
using type = mp11::mp_any_of<states, has_deferred_event>;
|
||||
};
|
||||
template <typename StateOrStates, typename Event>
|
||||
using has_deferred_event = typename has_deferred_event_impl<StateOrStates, Event>::type;
|
||||
|
||||
// event used internally for wrapping a direct entry
|
||||
template <class State, class Event>
|
||||
@@ -357,31 +367,6 @@ struct direct_entry_event
|
||||
Event const& m_event;
|
||||
};
|
||||
|
||||
//returns the owner of an explicit_entry state
|
||||
//which is the containing SM if the transition originates from outside the containing SM
|
||||
//or else the explicit_entry state itself
|
||||
template <class State,class ContainingSM>
|
||||
struct get_owner
|
||||
{
|
||||
using type = mp11::mp_if<
|
||||
mp11::mp_same<typename State::owner, ContainingSM>,
|
||||
State,
|
||||
typename State::owner
|
||||
>;
|
||||
};
|
||||
|
||||
|
||||
template <class Sequence, class ContainingSM>
|
||||
struct get_fork_owner
|
||||
{
|
||||
typedef typename ::boost::mpl::front<Sequence>::type seq_front;
|
||||
typedef typename ::boost::mpl::if_<
|
||||
typename ::boost::mpl::not_<
|
||||
typename ::boost::is_same<typename seq_front::owner,ContainingSM>::type>::type,
|
||||
typename seq_front::owner,
|
||||
seq_front >::type type;
|
||||
};
|
||||
|
||||
// builds flags (add internal_flag_list and flag_list). internal_flag_list is used for terminate/interrupt states
|
||||
template <class State>
|
||||
struct get_flag_list
|
||||
|
||||
47
include/boost/msm/backmp11/detail/state_tags.hpp
Normal file
47
include/boost/msm/backmp11/detail/state_tags.hpp
Normal file
@@ -0,0 +1,47 @@
|
||||
// Copyright 2025 Christian Granzin
|
||||
// Copyright 2008 Christophe Henry
|
||||
// henry UNDERSCORE christophe AT hotmail DOT com
|
||||
// This is an extended version of the state machine available in the boost::mpl library
|
||||
// Distributed under the same license as the original.
|
||||
// Copyright for the original version:
|
||||
// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed
|
||||
// under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_MSM_BACKMP11_DETAIL_STATE_TAGS_HPP
|
||||
#define BOOST_MSM_BACKMP11_DETAIL_STATE_TAGS_HPP
|
||||
|
||||
#include <boost/mp11.hpp>
|
||||
|
||||
#include <boost/msm/front/detail/state_tags.hpp>
|
||||
|
||||
namespace boost::msm::backmp11::detail
|
||||
{
|
||||
|
||||
// States
|
||||
struct state_machine_tag {};
|
||||
template <typename T>
|
||||
using has_state_machine_tag = std::is_same<typename T::internal::tag, state_machine_tag>;
|
||||
template <typename T>
|
||||
using is_composite = mp11::mp_or<
|
||||
std::is_same<typename T::internal::tag, msm::front::detail::composite_state_tag>,
|
||||
has_state_machine_tag<T>
|
||||
>;
|
||||
|
||||
// Pseudostates
|
||||
struct explicit_entry_be_tag {};
|
||||
template <typename T>
|
||||
using has_explicit_entry_be_tag = std::is_same<typename T::internal::tag, explicit_entry_be_tag>;
|
||||
|
||||
struct entry_pseudostate_be_tag {};
|
||||
template <typename T>
|
||||
using has_entry_pseudostate_be_tag = std::is_same<typename T::internal::tag, entry_pseudostate_be_tag>;
|
||||
|
||||
struct exit_pseudostate_be_tag {};
|
||||
template <typename T>
|
||||
using has_exit_pseudostate_be_tag = std::is_same<typename T::internal::tag, exit_pseudostate_be_tag>;
|
||||
|
||||
} // namespace boost::msm::backmp11::detail
|
||||
|
||||
#endif // BOOST_MSM_BACKMP11_DETAIL_STATE_TAGS_HPP
|
||||
@@ -17,54 +17,58 @@
|
||||
namespace boost::msm::backmp11::detail
|
||||
{
|
||||
|
||||
// Helper to provide a zero-cost abstraction if Predicate is always_true.
|
||||
template <typename List, template <typename> typename Predicate>
|
||||
struct copy_if_impl
|
||||
// Helper to apply multiple predicates sequentially.
|
||||
template <typename List, template <typename> typename... Predicates>
|
||||
struct copy_if_impl;
|
||||
template <typename List, template <typename> typename Predicate, template <typename> typename... Predicates>
|
||||
struct copy_if_impl<List, Predicate, Predicates...>
|
||||
{
|
||||
using type = mp11::mp_copy_if<List, Predicate>;
|
||||
using subset = mp11::mp_copy_if<List, Predicate>;
|
||||
using type = typename copy_if_impl<subset, Predicates...>::type;
|
||||
};
|
||||
template <typename List>
|
||||
struct copy_if_impl<List, always_true>
|
||||
struct copy_if_impl<List>
|
||||
{
|
||||
using type = List;
|
||||
};
|
||||
template<typename List, template <typename> typename Predicate>
|
||||
using copy_if = typename copy_if_impl<List, Predicate>::type;
|
||||
template<typename List, template <typename> typename... Predicates>
|
||||
using copy_if = typename copy_if_impl<List, Predicates...>::type;
|
||||
|
||||
template<typename StateMachine, template <typename> typename Predicate>
|
||||
using state_subset = copy_if<typename StateMachine::internal::state_set, Predicate>;
|
||||
template<typename StateMachine, template <typename> typename... Predicates>
|
||||
using state_subset = copy_if<typename StateMachine::internal::state_set, Predicates...>;
|
||||
|
||||
// State visitor implementation.
|
||||
// States to visit can be selected with VisitMode
|
||||
// and additionally compile-time filtered with Predicate.
|
||||
// States to visit can be selected with Mode
|
||||
// and additionally compile-time filtered with Predicates.
|
||||
template <
|
||||
typename StateMachine,
|
||||
typename Visitor,
|
||||
visit_mode Mode,
|
||||
template <typename> typename Predicate = always_true,
|
||||
typename Enable = void>
|
||||
class state_visitor;
|
||||
typename Enable = void,
|
||||
template <typename> typename... Predicates
|
||||
>
|
||||
class state_visitor_impl;
|
||||
|
||||
template <
|
||||
typename StateMachine,
|
||||
typename Visitor,
|
||||
visit_mode Mode,
|
||||
template <typename> typename Predicate>
|
||||
class state_visitor<
|
||||
template <typename> typename... Predicates>
|
||||
class state_visitor_impl<
|
||||
StateMachine,
|
||||
Visitor,
|
||||
Mode,
|
||||
Predicate,
|
||||
std::enable_if_t<
|
||||
has_flag(Mode, visit_mode::active_states) &&
|
||||
mp11::mp_not<mp11::mp_empty<state_subset<StateMachine, Predicate>>>::value>>
|
||||
mp11::mp_not<mp11::mp_empty<state_subset<StateMachine, Predicates...>>>::value>,
|
||||
Predicates...>
|
||||
{
|
||||
public:
|
||||
state_visitor()
|
||||
state_visitor_impl()
|
||||
{
|
||||
using state_identities =
|
||||
mp11::mp_transform<mp11::mp_identity,
|
||||
state_subset<StateMachine, Predicate>>;
|
||||
state_subset<StateMachine, Predicates...>>;
|
||||
mp11::mp_for_each<state_identities>(
|
||||
[this](auto state_identity)
|
||||
{
|
||||
@@ -78,7 +82,7 @@ class state_visitor<
|
||||
{
|
||||
if (sm.m_running)
|
||||
{
|
||||
const state_visitor& self = instance();
|
||||
const state_visitor_impl& self = instance();
|
||||
for (const int state_id : sm.m_active_state_ids)
|
||||
{
|
||||
self.dispatch(sm, state_id, std::forward<Visitor>(visitor));
|
||||
@@ -96,16 +100,16 @@ class state_visitor<
|
||||
auto& state = sm.template get_state<State>();
|
||||
visitor(state);
|
||||
|
||||
if constexpr (has_back_end_tag<State>::value &&
|
||||
if constexpr (has_state_machine_tag<State>::value &&
|
||||
has_flag(Mode, visit_mode::recursive))
|
||||
{
|
||||
state.template visit_if<Predicate, Mode>(std::forward<Visitor>(visitor));
|
||||
state.template visit_if<Mode, Predicates...>(std::forward<Visitor>(visitor));
|
||||
}
|
||||
}
|
||||
|
||||
static const state_visitor& instance()
|
||||
static const state_visitor_impl& instance()
|
||||
{
|
||||
static const state_visitor instance;
|
||||
static const state_visitor_impl instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
@@ -125,22 +129,22 @@ template <
|
||||
typename StateMachine,
|
||||
typename Visitor,
|
||||
visit_mode Mode,
|
||||
template <typename> typename Predicate>
|
||||
class state_visitor<
|
||||
template <typename> typename... Predicates>
|
||||
class state_visitor_impl<
|
||||
StateMachine,
|
||||
Visitor,
|
||||
Mode,
|
||||
Predicate,
|
||||
std::enable_if_t<
|
||||
has_flag(Mode, visit_mode::all_states) &&
|
||||
mp11::mp_not<mp11::mp_empty<state_subset<StateMachine, Predicate>>>::value>>
|
||||
mp11::mp_not<mp11::mp_empty<state_subset<StateMachine, Predicates...>>>::value>,
|
||||
Predicates...>
|
||||
{
|
||||
public:
|
||||
static void visit(StateMachine& sm, Visitor visitor)
|
||||
{
|
||||
using state_identities =
|
||||
mp11::mp_transform<mp11::mp_identity,
|
||||
state_subset<StateMachine, Predicate>>;
|
||||
state_subset<StateMachine, Predicates...>>;
|
||||
mp11::mp_for_each<state_identities>(
|
||||
[&sm, &visitor](auto state_identity)
|
||||
{
|
||||
@@ -148,10 +152,10 @@ class state_visitor<
|
||||
auto& state = sm.template get_state<State>();
|
||||
visitor(state);
|
||||
|
||||
if constexpr (is_back_end<State>::value &&
|
||||
if constexpr (has_state_machine_tag<State>::value &&
|
||||
has_flag(Mode, visit_mode::recursive))
|
||||
{
|
||||
state.template visit_if<Predicate, Mode>(std::forward<Visitor>(visitor));
|
||||
state.template visit_if<Mode, Predicates...>(std::forward<Visitor>(visitor));
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -162,14 +166,14 @@ template <
|
||||
typename StateMachine,
|
||||
typename Visitor,
|
||||
visit_mode Mode,
|
||||
template <typename> typename Predicate>
|
||||
class state_visitor<
|
||||
template <typename> typename... Predicates>
|
||||
class state_visitor_impl<
|
||||
StateMachine,
|
||||
Visitor,
|
||||
Mode,
|
||||
Predicate,
|
||||
std::enable_if_t<
|
||||
mp11::mp_empty<state_subset<StateMachine, Predicate>>::value>>
|
||||
mp11::mp_empty<state_subset<StateMachine, Predicates...>>::value>,
|
||||
Predicates...>
|
||||
{
|
||||
public:
|
||||
static constexpr void visit(StateMachine&, Visitor)
|
||||
@@ -177,6 +181,13 @@ class state_visitor<
|
||||
}
|
||||
};
|
||||
|
||||
template <
|
||||
typename StateMachine,
|
||||
typename Visitor,
|
||||
visit_mode Mode,
|
||||
template <typename> typename... Predicates>
|
||||
using state_visitor = state_visitor_impl<StateMachine, Visitor, Mode, void, Predicates...>;
|
||||
|
||||
} // boost::msm::backmp11::detail
|
||||
|
||||
#endif // BOOST_MSM_BACKMP11_DETAIL_STATE_VISITOR_HPP
|
||||
|
||||
86
include/boost/msm/backmp11/detail/transition_table.hpp
Normal file
86
include/boost/msm/backmp11/detail/transition_table.hpp
Normal file
@@ -0,0 +1,86 @@
|
||||
// Copyright 2025 Christian Granzin
|
||||
// Copyright 2008 Christophe Henry
|
||||
// henry UNDERSCORE christophe AT hotmail DOT com
|
||||
// This is an extended version of the state machine available in the boost::mpl library
|
||||
// Distributed under the same license as the original.
|
||||
// Copyright for the original version:
|
||||
// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed
|
||||
// under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_MSM_BACKMP11_TRANSITION_TABLE_HPP
|
||||
#define BOOST_MSM_BACKMP11_TRANSITION_TABLE_HPP
|
||||
|
||||
#include <boost/msm/backmp11/detail/metafunctions.hpp>
|
||||
|
||||
namespace boost::msm::backmp11::detail
|
||||
{
|
||||
|
||||
// returns the transition table of a Composite state
|
||||
template <class Derived>
|
||||
struct get_transition_table
|
||||
{
|
||||
using Stt = typename Derived::internal::template create_real_stt<typename Derived::front_end_t>::type;
|
||||
using type = Stt;
|
||||
};
|
||||
template<typename T>
|
||||
using get_transition_table_t = typename get_transition_table<T>::type;
|
||||
|
||||
// recursively builds an internal table including those of substates, sub-substates etc.
|
||||
// variant for submachines
|
||||
template <class State, bool IsComposite>
|
||||
struct recursive_get_internal_transition_table
|
||||
{
|
||||
// get the composite's internal table
|
||||
typedef typename State::front_end_t::internal_transition_table composite_table;
|
||||
// and for every substate (state of submachine), recursively get the internal transition table
|
||||
using composite_states = typename State::internal::state_set;
|
||||
template<typename V, typename SubState>
|
||||
using append_recursive_internal_transition_table = mp11::mp_append<
|
||||
V,
|
||||
typename recursive_get_internal_transition_table<SubState, has_state_machine_tag<SubState>::value>::type
|
||||
>;
|
||||
typedef typename mp11::mp_fold<
|
||||
composite_states,
|
||||
to_mp_list_t<composite_table>,
|
||||
append_recursive_internal_transition_table
|
||||
> type;
|
||||
};
|
||||
// stop iterating on leafs (simple states)
|
||||
template <class State>
|
||||
struct recursive_get_internal_transition_table<State, false>
|
||||
{
|
||||
typedef to_mp_list_t<
|
||||
typename State::internal_transition_table
|
||||
> type;
|
||||
};
|
||||
// recursively get a transition table for a given state machine.
|
||||
// returns the transition table for this state + the tables of all submachines recursively.
|
||||
template <class Composite>
|
||||
struct recursive_get_transition_table
|
||||
{
|
||||
// get the transition table of the state if it's a state machine
|
||||
typedef typename mp11::mp_eval_if_c<
|
||||
!has_state_machine_tag<Composite>::value,
|
||||
mp11::mp_list<>,
|
||||
get_transition_table_t,
|
||||
Composite
|
||||
> org_table;
|
||||
|
||||
// and for every substate, recursively get the transition table if it's a state machine
|
||||
using submachines = mp11::mp_copy_if<generate_state_set<Composite>, has_state_machine_tag>;
|
||||
template<typename V, typename T>
|
||||
using append_recursive_transition_table = mp11::mp_append<
|
||||
V,
|
||||
typename recursive_get_transition_table<T>::type
|
||||
>;
|
||||
typedef typename mp11::mp_fold<
|
||||
submachines,
|
||||
org_table,
|
||||
append_recursive_transition_table> type;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // BOOST_MSM_BACKMP11_TRANSITION_TABLE_HPP
|
||||
@@ -50,6 +50,10 @@ struct compile_policy_impl<favor_compile_time>
|
||||
{
|
||||
using add_forwarding_rows = mp11::mp_false;
|
||||
|
||||
// Bitmask for process result checks.
|
||||
static constexpr process_result handled_or_deferred =
|
||||
process_result::HANDLED_TRUE | process_result::HANDLED_DEFERRED;
|
||||
|
||||
static bool is_completion_event(const any_event& event)
|
||||
{
|
||||
return (event.type() == typeid(front::none));
|
||||
@@ -62,15 +66,15 @@ struct compile_policy_impl<favor_compile_time>
|
||||
return helper.is_end_interrupt_event(event);
|
||||
}
|
||||
|
||||
template <typename StateMachine, typename Event>
|
||||
static HandledEnum process_event_internal(StateMachine& sm, const Event& event, EventSource source)
|
||||
template <typename Event>
|
||||
static any_event normalize_event(const Event& event)
|
||||
{
|
||||
return sm.process_event_internal_impl(any_event(event), source);
|
||||
return any_event{event};
|
||||
}
|
||||
template <typename StateMachine>
|
||||
static HandledEnum process_event_internal(StateMachine& sm, const any_event& event, EventSource source)
|
||||
|
||||
constexpr static const any_event& normalize_event(const any_event& event)
|
||||
{
|
||||
return sm.process_event_internal_impl(event, source);
|
||||
return event;
|
||||
}
|
||||
|
||||
template <typename StateMachine>
|
||||
@@ -84,8 +88,7 @@ struct compile_policy_impl<favor_compile_time>
|
||||
|
||||
process_result process() override
|
||||
{
|
||||
return process_event_internal(
|
||||
m_sm,
|
||||
return m_sm.process_event_internal(
|
||||
m_event,
|
||||
EventSource::EVENT_SOURCE_DEFERRED);
|
||||
}
|
||||
@@ -100,6 +103,11 @@ struct compile_policy_impl<favor_compile_time>
|
||||
any_event m_event;
|
||||
};
|
||||
|
||||
// For this policy we cannot determine whether a specific event
|
||||
// is deferred at compile-time, as all events are any_events.
|
||||
template <typename StateOrStates, typename Event>
|
||||
using has_deferred_event = has_deferred_events<StateOrStates>;
|
||||
|
||||
template <typename State>
|
||||
static const std::unordered_set<std::type_index>& get_deferred_event_type_indices()
|
||||
{
|
||||
@@ -123,15 +131,16 @@ struct compile_policy_impl<favor_compile_time>
|
||||
template <typename StateMachine>
|
||||
static bool is_event_deferred(const StateMachine& sm, const any_event& event)
|
||||
{
|
||||
const std::type_index type_index = event.type();
|
||||
bool result = false;
|
||||
auto visitor = [&result, type_index](auto& state) {
|
||||
const std::type_index type_index = event.type();
|
||||
auto visitor = [&result, type_index](const auto& state)
|
||||
{
|
||||
using State = std::decay_t<decltype(state)>;
|
||||
const auto& set = get_deferred_event_type_indices<State>();
|
||||
result |= (set.find(type_index) != set.end());
|
||||
};
|
||||
sm.template visit_if<has_deferred_events,
|
||||
visit_mode::active_non_recursive>(visitor);
|
||||
sm.template visit_if<visit_mode::active_non_recursive,
|
||||
has_deferred_events>(visitor);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -143,14 +152,6 @@ struct compile_policy_impl<favor_compile_time>
|
||||
new deferred_event_impl(sm, event, deferred_events.cur_seq_cnt)});
|
||||
}
|
||||
|
||||
template<typename Stt>
|
||||
struct get_real_rows
|
||||
{
|
||||
template<typename Transition>
|
||||
using is_real_row = mp11::mp_not<typename has_not_real_row_tag<Transition>::type>;
|
||||
typedef mp11::mp_copy_if<Stt, is_real_row> type;
|
||||
};
|
||||
|
||||
// Convert an event to a type index.
|
||||
template<class Event>
|
||||
static std::type_index to_type_index()
|
||||
@@ -165,7 +166,8 @@ struct compile_policy_impl<favor_compile_time>
|
||||
template<class StateMachine>
|
||||
end_interrupt_event_helper(const StateMachine& sm)
|
||||
{
|
||||
mp11::mp_for_each<mp11::mp_transform<mp11::mp_identity, typename StateMachine::event_set_mp11>>(
|
||||
typedef typename generate_event_set<typename StateMachine::internal::stt>::event_set_mp11 event_set_mp11;
|
||||
mp11::mp_for_each<mp11::mp_transform<mp11::mp_identity, event_set_mp11>>(
|
||||
[this, &sm](auto event_identity)
|
||||
{
|
||||
using Event = typename decltype(event_identity)::type;
|
||||
@@ -190,136 +192,151 @@ struct compile_policy_impl<favor_compile_time>
|
||||
map m_is_flag_active_functions;
|
||||
};
|
||||
|
||||
struct chain_row
|
||||
// Class used to build a chain of transitions for a given event and state.
|
||||
// Allows transition conflicts.
|
||||
class transition_chain
|
||||
{
|
||||
template<typename Fsm>
|
||||
HandledEnum operator()(Fsm& fsm, int region, int state, any_event const& evt) const
|
||||
public:
|
||||
template<typename StateMachine>
|
||||
process_result execute(StateMachine& sm, int& state_id, any_event const& event) const
|
||||
{
|
||||
typedef HandledEnum (*real_cell)(Fsm&, int, int, any_event const&);
|
||||
HandledEnum res = HandledEnum::HANDLED_FALSE;
|
||||
typename std::deque<generic_cell>::const_iterator it = one_state.begin();
|
||||
while (it != one_state.end() && (res != HandledEnum::HANDLED_TRUE && res != HandledEnum::HANDLED_DEFERRED ))
|
||||
using real_cell = process_result (*)(StateMachine&, int&, any_event const&);
|
||||
process_result result = process_result::HANDLED_FALSE;
|
||||
for (const generic_cell function : m_transition_functions)
|
||||
{
|
||||
auto fnc = reinterpret_cast<real_cell>(*it);
|
||||
HandledEnum handled = (*fnc)(fsm,region,state,evt);
|
||||
// reject is considered as erasing an error (HANDLED_FALSE)
|
||||
if ((HandledEnum::HANDLED_FALSE==handled) && (HandledEnum::HANDLED_GUARD_REJECT==res) )
|
||||
res = HandledEnum::HANDLED_GUARD_REJECT;
|
||||
else
|
||||
res = handled;
|
||||
++it;
|
||||
result |= reinterpret_cast<real_cell>(function)(sm, state_id, event);
|
||||
if (result & handled_or_deferred)
|
||||
{
|
||||
// If a guard rejected previously, ensure this bit is not present.
|
||||
return result & handled_or_deferred;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
// Use a deque with a generic type to avoid unnecessary template instantiations.
|
||||
std::deque<generic_cell> one_state;
|
||||
// At this point result can be HANDLED_FALSE or HANDLED_GUARD_REJECT.
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename StateMachine, typename Event, typename Transition>
|
||||
void add_transition()
|
||||
{
|
||||
m_transition_functions.emplace_front(reinterpret_cast<generic_cell>(
|
||||
&convert_event_and_execute<StateMachine, Event, Transition>));
|
||||
}
|
||||
|
||||
private:
|
||||
// Adapter for calling a transition's execute function.
|
||||
template<typename StateMachine, typename Event, typename Transition>
|
||||
static process_result convert_event_and_execute(
|
||||
StateMachine& sm, int& state_id, const any_event& event)
|
||||
{
|
||||
return Transition::execute(sm, state_id, *any_cast<Event>(&event));
|
||||
}
|
||||
|
||||
std::deque<generic_cell> m_transition_functions;
|
||||
};
|
||||
|
||||
// Generates a singleton runtime lookup table that maps current state
|
||||
// to a function that makes the SM take its transition on the given
|
||||
// Event type.
|
||||
template<class Fsm>
|
||||
template<class StateMachine>
|
||||
class dispatch_table
|
||||
{
|
||||
using Stt = typename Fsm::complete_table;
|
||||
public:
|
||||
// Dispatch an event.
|
||||
static HandledEnum dispatch(Fsm& fsm, int region_id, int state_id, const any_event& event)
|
||||
static process_result dispatch(StateMachine& sm, int& state_id, const any_event& event)
|
||||
{
|
||||
return instance().m_state_dispatch_tables[state_id+1].dispatch(fsm, region_id, state_id, event);
|
||||
return instance().m_state_dispatch_tables[state_id+1].dispatch(sm, state_id, event);
|
||||
}
|
||||
|
||||
// Dispatch an event to the FSM's internal table.
|
||||
static HandledEnum dispatch_internal(Fsm& fsm, int region_id, int state_id, const any_event& event)
|
||||
// Dispatch an event to the SM's internal table.
|
||||
static process_result dispatch_internal(StateMachine& sm, const any_event& event)
|
||||
{
|
||||
return instance().m_state_dispatch_tables[0].dispatch(fsm, region_id, state_id, event);
|
||||
int no_state_id;
|
||||
return instance().m_state_dispatch_tables[0].dispatch(sm, no_state_id, event);
|
||||
}
|
||||
|
||||
private:
|
||||
// Adapter for calling a row's execute function.
|
||||
template<typename Event, typename Row>
|
||||
static HandledEnum convert_and_execute(Fsm& fsm, int region_id, int state_id, const any_event& event)
|
||||
{
|
||||
return Row::execute(fsm, region_id, state_id, *any_cast<Event>(&event));
|
||||
}
|
||||
using state_set = typename StateMachine::internal::state_set;
|
||||
using Stt = typename StateMachine::complete_table;
|
||||
|
||||
// Dispatch table for one state.
|
||||
class state_dispatch_table
|
||||
{
|
||||
public:
|
||||
// Initialize the submachine call for the given state.
|
||||
template<typename State>
|
||||
void init_call_submachine()
|
||||
// Initialize the call to the composite state's process_event function.
|
||||
template<typename CompositeState>
|
||||
void init_composite_state()
|
||||
{
|
||||
m_call_submachine = [](Fsm& fsm, const any_event& evt)
|
||||
{
|
||||
return (fsm.template get_state<State&>()).process_event_internal(evt);
|
||||
};
|
||||
m_call_process_event = &call_process_event<CompositeState>;
|
||||
}
|
||||
|
||||
template<typename Event>
|
||||
chain_row& get_chain_row()
|
||||
// Add a transition to the dispatch table.
|
||||
template<typename Event, typename Transition>
|
||||
void add_transition()
|
||||
{
|
||||
return m_entries[to_type_index<Event>()];
|
||||
transition_chain& chain = m_transition_chains[to_type_index<Event>()];
|
||||
chain.add_transition<StateMachine, Event, Transition>();
|
||||
}
|
||||
|
||||
// Dispatch an event.
|
||||
HandledEnum dispatch(Fsm& fsm, int region_id, int state_id, const any_event& event) const
|
||||
process_result dispatch(StateMachine& sm, int& state_id, const any_event& event) const
|
||||
{
|
||||
HandledEnum handled = HandledEnum::HANDLED_FALSE;
|
||||
if (m_call_submachine)
|
||||
process_result result = process_result::HANDLED_FALSE;
|
||||
if (m_call_process_event)
|
||||
{
|
||||
handled = m_call_submachine(fsm, event);
|
||||
if (handled)
|
||||
result = m_call_process_event(sm, event);
|
||||
if (result)
|
||||
{
|
||||
return handled;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
auto it = m_entries.find(event.type());
|
||||
if (it != m_entries.end())
|
||||
auto it = m_transition_chains.find(event.type());
|
||||
if (it != m_transition_chains.end())
|
||||
{
|
||||
handled = (it->second)(fsm, region_id, state_id, event);
|
||||
result = (it->second.execute)(sm, state_id, event);
|
||||
}
|
||||
return handled;
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
std::unordered_map<std::type_index, chain_row> m_entries;
|
||||
// Special functor if the state is a composite
|
||||
std::function<HandledEnum(Fsm&, const any_event&)> m_call_submachine;
|
||||
template <typename CompositeState>
|
||||
static process_result call_process_event(StateMachine& sm, const any_event& event)
|
||||
{
|
||||
return sm.template get_state<CompositeState&>().process_event_internal(event);
|
||||
}
|
||||
|
||||
std::unordered_map<std::type_index, transition_chain> m_transition_chains;
|
||||
// Optional method in case the state is a composite.
|
||||
process_result (*m_call_process_event)(StateMachine&, const any_event&){nullptr};
|
||||
};
|
||||
|
||||
dispatch_table()
|
||||
{
|
||||
// Execute row-specific initializations.
|
||||
mp11::mp_for_each<typename get_real_rows<Stt>::type>(
|
||||
[this](auto row)
|
||||
mp11::mp_for_each<Stt>(
|
||||
[this](auto transition)
|
||||
{
|
||||
using Row = decltype(row);
|
||||
using Event = typename Row::transition_event;
|
||||
using State = typename Row::current_state_type;
|
||||
static constexpr int state_id = Fsm::template get_state_id<State>();
|
||||
auto& chain_row = m_state_dispatch_tables[state_id + 1].template get_chain_row<Event>();
|
||||
chain_row.one_state.push_front(reinterpret_cast<generic_cell>(&convert_and_execute<Event, Row>));
|
||||
using Transition = decltype(transition);
|
||||
using Event = typename Transition::transition_event;
|
||||
using State = typename Transition::current_state_type;
|
||||
static constexpr int state_id = StateMachine::template get_state_id<State>();
|
||||
m_state_dispatch_tables[state_id + 1].template add_transition<Event, Transition>();
|
||||
});
|
||||
|
||||
// Execute state-specific initializations.
|
||||
using submachine_states = mp11::mp_copy_if<state_set, has_back_end_tag>;
|
||||
using submachine_states = mp11::mp_copy_if<state_set, has_state_machine_tag>;
|
||||
mp11::mp_for_each<mp11::mp_transform<mp11::mp_identity, submachine_states>>(
|
||||
[this](auto state_identity)
|
||||
{
|
||||
using SubmachineState = typename decltype(state_identity)::type;
|
||||
static constexpr int state_id = Fsm::template get_state_id<SubmachineState>();
|
||||
m_state_dispatch_tables[state_id + 1].template init_call_submachine<SubmachineState>();
|
||||
using CompositeState = typename decltype(state_identity)::type;
|
||||
static constexpr int state_id = StateMachine::template get_state_id<CompositeState>();
|
||||
m_state_dispatch_tables[state_id + 1].template init_composite_state<CompositeState>();
|
||||
});
|
||||
}
|
||||
|
||||
// The singleton instance.
|
||||
static const dispatch_table& instance();
|
||||
|
||||
// Compute the maximum state value in the sm so we know how big
|
||||
// to make the table
|
||||
typedef typename generate_state_set<Stt>::state_set state_set;
|
||||
// We need one dispatch table per state, plus one for internal transitions of the SM.
|
||||
BOOST_STATIC_CONSTANT(int, max_state = (mp11::mp_size<state_set>::value));
|
||||
state_dispatch_table m_state_dispatch_tables[max_state+1];
|
||||
};
|
||||
@@ -327,9 +344,9 @@ struct compile_policy_impl<favor_compile_time>
|
||||
|
||||
#ifndef BOOST_MSM_BACKMP11_MANUAL_GENERATION
|
||||
|
||||
template<class Fsm>
|
||||
const typename compile_policy_impl<favor_compile_time>::template dispatch_table<Fsm>&
|
||||
compile_policy_impl<favor_compile_time>::dispatch_table<Fsm>::instance()
|
||||
template<class StateMachine>
|
||||
const typename compile_policy_impl<favor_compile_time>::template dispatch_table<StateMachine>&
|
||||
compile_policy_impl<favor_compile_time>::dispatch_table<StateMachine>::instance()
|
||||
{
|
||||
static dispatch_table table;
|
||||
return table;
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
#include <boost/msm/backmp11/detail/history_impl.hpp>
|
||||
#include <boost/msm/backmp11/detail/favor_runtime_speed.hpp>
|
||||
#include <boost/msm/backmp11/detail/state_visitor.hpp>
|
||||
#include <boost/msm/backmp11/detail/transition_table.hpp>
|
||||
#include <boost/msm/backmp11/common_types.hpp>
|
||||
#include <boost/msm/backmp11/state_machine_config.hpp>
|
||||
|
||||
@@ -87,8 +88,11 @@ class state_machine_base : public FrontEnd
|
||||
struct exit_pt : public ExitPoint
|
||||
{
|
||||
// tags
|
||||
struct internal
|
||||
{
|
||||
using tag = exit_pseudostate_be_tag;
|
||||
};
|
||||
typedef ExitPoint wrapped_exit;
|
||||
typedef int pseudo_exit;
|
||||
typedef derived_t owner;
|
||||
typedef int no_automatic_create;
|
||||
typedef typename
|
||||
@@ -147,8 +151,11 @@ class state_machine_base : public FrontEnd
|
||||
struct entry_pt : public EntryPoint
|
||||
{
|
||||
// tags
|
||||
struct internal
|
||||
{
|
||||
using tag = entry_pseudostate_be_tag;
|
||||
};
|
||||
typedef EntryPoint wrapped_entry;
|
||||
typedef int pseudo_entry;
|
||||
typedef derived_t owner;
|
||||
typedef int no_automatic_create;
|
||||
};
|
||||
@@ -156,47 +163,22 @@ class state_machine_base : public FrontEnd
|
||||
struct direct : public EntryPoint
|
||||
{
|
||||
// tags
|
||||
struct internal
|
||||
{
|
||||
using tag = explicit_entry_be_tag;
|
||||
};
|
||||
typedef EntryPoint wrapped_entry;
|
||||
typedef int explicit_entry_state;
|
||||
typedef derived_t owner;
|
||||
typedef int no_automatic_create;
|
||||
};
|
||||
|
||||
struct internal
|
||||
{
|
||||
using tag = back_end_tag;
|
||||
using tag = state_machine_tag;
|
||||
|
||||
using initial_states = to_mp_list_t<typename front_end_t::initial_state>;
|
||||
static constexpr int nr_regions = mp11::mp_size<initial_states>::value;
|
||||
|
||||
template <class State, typename Enable = void>
|
||||
struct make_entry
|
||||
{
|
||||
using type = State;
|
||||
};
|
||||
template <class State>
|
||||
struct make_entry<State, std::enable_if_t<has_pseudo_entry<State>::value>>
|
||||
{
|
||||
using type = entry_pt<State>;
|
||||
};
|
||||
template <class State>
|
||||
struct make_entry<State, std::enable_if_t<has_direct_entry<State>::value>>
|
||||
{
|
||||
using type = direct<State>;
|
||||
};
|
||||
|
||||
template <class State, typename Enable = void>
|
||||
struct make_exit
|
||||
{
|
||||
using type = State;
|
||||
};
|
||||
template <class State>
|
||||
struct make_exit<State, std::enable_if_t<has_pseudo_exit<State>::value>>
|
||||
{
|
||||
using type = exit_pt<State>;
|
||||
};
|
||||
|
||||
|
||||
template<typename Row, bool HasGuard, typename Event, typename Source, typename Target>
|
||||
static bool call_guard_or_true(state_machine_base& sm, const Event& event, Source& source, Target& target)
|
||||
{
|
||||
@@ -227,42 +209,18 @@ class state_machine_base : public FrontEnd
|
||||
template<typename Row, bool HasAction, bool HasGuard>
|
||||
struct Transition
|
||||
{
|
||||
typedef typename Row::Evt transition_event;
|
||||
typedef typename make_entry<typename Row::Source>::type T1;
|
||||
// if the source is an exit pseudo state, then
|
||||
// current_state_type becomes the result of get_owner
|
||||
// meaning the containing SM from which the exit occurs
|
||||
typedef typename ::boost::mpl::eval_if<
|
||||
typename has_pseudo_exit<T1>::type,
|
||||
get_owner<T1,derived_t>,
|
||||
::boost::mpl::identity<typename Row::Source> >::type current_state_type;
|
||||
|
||||
typedef typename make_exit<typename Row::Target>::type T2;
|
||||
// if Target is a sequence, then we have a fork and expect a sequence of explicit_entry
|
||||
// else if Target is an explicit_entry, next_state_type becomes the result of get_owner
|
||||
// meaning the containing SM if the row is "outside" the containing SM or else the explicit_entry state itself
|
||||
typedef typename ::boost::mpl::eval_if<
|
||||
typename ::boost::mpl::is_sequence<T2>::type,
|
||||
get_fork_owner<T2,derived_t>,
|
||||
::boost::mpl::eval_if<
|
||||
typename has_no_automatic_create<T2>::type,
|
||||
get_owner<T2,derived_t>,
|
||||
::boost::mpl::identity<T2> >
|
||||
>::type next_state_type;
|
||||
using transition_event = typename Row::Evt;
|
||||
using current_state_type = convert_source_state<derived_t, typename Row::Source>;
|
||||
using next_state_type = convert_target_state<derived_t, typename Row::Target>;
|
||||
|
||||
// Take the transition action and return the next state.
|
||||
static process_result execute(state_machine_base& sm, int region_index, int state, transition_event const& event)
|
||||
static process_result execute(state_machine_base& sm,
|
||||
[[maybe_unused]] int& state_id,
|
||||
transition_event const& event)
|
||||
{
|
||||
BOOST_STATIC_CONSTANT(int, current_state = (get_state_id<current_state_type>()));
|
||||
BOOST_STATIC_CONSTANT(int, next_state = (get_state_id<next_state_type>()));
|
||||
boost::ignore_unused(state); // Avoid warnings if BOOST_ASSERT expands to nothing.
|
||||
BOOST_ASSERT(state == (current_state));
|
||||
// if T1 is an exit pseudo state, then take the transition only if the pseudo exit state is active
|
||||
if (has_pseudo_exit<T1>::type::value &&
|
||||
!sm.is_exit_state_active<T1, get_owner<T1,derived_t>>())
|
||||
{
|
||||
return process_result::HANDLED_FALSE;
|
||||
}
|
||||
static constexpr int current_state_id = get_state_id<current_state_type>();
|
||||
static constexpr int next_state_id = get_state_id<next_state_type>();
|
||||
BOOST_ASSERT(state_id == current_state_id);
|
||||
|
||||
auto& source = sm.get_state<current_state_type>();
|
||||
auto& target = sm.get_state<next_state_type>();
|
||||
@@ -272,19 +230,19 @@ class state_machine_base : public FrontEnd
|
||||
// guard rejected the event, we stay in the current one
|
||||
return process_result::HANDLED_GUARD_REJECT;
|
||||
}
|
||||
sm.m_active_state_ids[region_index] = active_state_switching::after_guard(current_state,next_state);
|
||||
state_id = active_state_switching::after_guard(current_state_id,next_state_id);
|
||||
|
||||
// first call the exit method of the current state
|
||||
source.on_exit(event, sm.get_fsm_argument());
|
||||
sm.m_active_state_ids[region_index] = active_state_switching::after_exit(current_state,next_state);
|
||||
state_id = active_state_switching::after_exit(current_state_id,next_state_id);
|
||||
|
||||
// then call the action method
|
||||
process_result res = call_action_or_true<Row, HasAction>(sm, event, source, target);
|
||||
sm.m_active_state_ids[region_index] = active_state_switching::after_action(current_state,next_state);
|
||||
state_id = active_state_switching::after_action(current_state_id,next_state_id);
|
||||
|
||||
// and finally the entry method of the new current state
|
||||
convert_event_and_execute_entry<T2>(target,event,sm);
|
||||
sm.m_active_state_ids[region_index] = active_state_switching::after_entry(current_state,next_state);
|
||||
convert_event_and_execute_entry<typename Row::Target>(target,event,sm);
|
||||
state_id = active_state_switching::after_entry(current_state_id,next_state_id);
|
||||
|
||||
return res;
|
||||
}
|
||||
@@ -300,12 +258,11 @@ class state_machine_base : public FrontEnd
|
||||
typedef current_state_type next_state_type;
|
||||
|
||||
// Take the transition action and return the next state.
|
||||
static process_result execute(state_machine_base& sm, int , int state, transition_event const& event)
|
||||
static process_result execute(state_machine_base& sm,
|
||||
[[maybe_unused]] int& state_id,
|
||||
transition_event const& event)
|
||||
{
|
||||
|
||||
BOOST_STATIC_CONSTANT(int, current_state = (get_state_id<current_state_type>()));
|
||||
boost::ignore_unused(state, current_state); // Avoid warnings if BOOST_ASSERT expands to nothing.
|
||||
BOOST_ASSERT(state == (current_state));
|
||||
BOOST_ASSERT(state_id == get_state_id<current_state_type>());
|
||||
|
||||
auto& source = sm.get_state<current_state_type>();
|
||||
auto& target = source;
|
||||
@@ -401,8 +358,8 @@ class state_machine_base : public FrontEnd
|
||||
> type;
|
||||
};
|
||||
|
||||
using state_set = generate_state_set<state_machine_base>;
|
||||
using stt = typename get_transition_table<state_machine_base>::type;
|
||||
using state_set = typename generate_state_set<stt>::state_set;
|
||||
};
|
||||
|
||||
typedef mp11::mp_rename<typename internal::state_set, std::tuple> states_t;
|
||||
@@ -436,8 +393,8 @@ class state_machine_base : public FrontEnd
|
||||
template <typename Policy>
|
||||
friend struct detail::compile_policy_impl;
|
||||
|
||||
template <typename, typename, visit_mode, template <typename> typename, typename>
|
||||
friend class state_visitor;
|
||||
template <typename, typename, visit_mode, typename, template <typename> typename...>
|
||||
friend class state_visitor_impl;
|
||||
|
||||
// Allow access to private members for serialization.
|
||||
// WARNING:
|
||||
@@ -467,21 +424,18 @@ class state_machine_base : public FrontEnd
|
||||
typedef int is_frow;
|
||||
|
||||
// Take the transition action and return the next state.
|
||||
static process_result execute(state_machine_base& sm, int region_index, int , transition_event const& event)
|
||||
static process_result execute(state_machine_base& sm, int& state_id, transition_event const& event)
|
||||
{
|
||||
// false as second parameter because this event is forwarded from outer fsm
|
||||
process_result res =
|
||||
(sm.get_state<current_state_type>()).process_event_internal(event);
|
||||
sm.m_active_state_ids[region_index]=get_state_id<T1>();
|
||||
return res;
|
||||
process_result result =
|
||||
sm.get_state<current_state_type>().process_event_internal(event);
|
||||
state_id = get_state_id<T1>();
|
||||
return result;
|
||||
}
|
||||
// helper metafunctions used by dispatch table and give the frow a new event
|
||||
// (used to avoid double entries in a table because of base events)
|
||||
template <class NewEvent>
|
||||
struct replace_event
|
||||
{
|
||||
typedef frow<T1,NewEvent> type;
|
||||
};
|
||||
using replace_event = frow<T1, NewEvent>;
|
||||
};
|
||||
|
||||
template <class Table,class Intermediate,class State>
|
||||
@@ -530,7 +484,9 @@ class state_machine_base : public FrontEnd
|
||||
};
|
||||
|
||||
typedef typename generate_state_map<state_set>::type state_map_mp11;
|
||||
typedef typename generate_event_set<stt>::event_set_mp11 event_set_mp11;
|
||||
// TODO:
|
||||
// Recursion somewhere, can't define this in the SM (yet).
|
||||
// typedef typename generate_event_set<stt>::event_set_mp11 event_set_mp11;
|
||||
typedef history_impl<typename front_end_t::history, nr_regions> concrete_history;
|
||||
typedef typename generate_event_set<
|
||||
typename create_real_stt<front_end_t, typename front_end_t::internal_transition_table >::type
|
||||
@@ -585,11 +541,12 @@ class state_machine_base : public FrontEnd
|
||||
deferred_events_queue_t queue;
|
||||
size_t cur_seq_cnt;
|
||||
};
|
||||
using has_any_deferred_event =
|
||||
mp11::mp_any_of<state_set, has_deferred_events>;
|
||||
using deferring_states = mp11::mp_copy_if<state_set, has_deferred_events>;
|
||||
using has_deferring_states =
|
||||
mp11::mp_not<mp11::mp_empty<deferring_states>>;
|
||||
using deferred_events_member =
|
||||
optional_instance<deferred_events_t,
|
||||
has_any_deferred_event::value ||
|
||||
has_deferring_states::value ||
|
||||
has_activate_deferred_events<front_end_t>::value>;
|
||||
using events_queue_member =
|
||||
optional_instance<events_queue_t,
|
||||
@@ -615,18 +572,18 @@ class state_machine_base : public FrontEnd
|
||||
}
|
||||
|
||||
// Visit states with a compile-time filter (reduces template instantiations).
|
||||
template <template <typename> typename Predicate, visit_mode Mode, typename Visitor>
|
||||
// Kept private for now, because the API of this method is not stable yet
|
||||
// and this optimization is likely not needed to be available in the public API.
|
||||
template <visit_mode Mode, template <typename> typename... Predicates, typename Visitor>
|
||||
void visit_if(Visitor&& visitor)
|
||||
{
|
||||
using state_visitor = state_visitor<state_machine_base, Visitor, Mode, Predicate>;
|
||||
using state_visitor = state_visitor<state_machine_base, Visitor, Mode, Predicates...>;
|
||||
state_visitor::visit(*this, std::forward<Visitor>(visitor));
|
||||
}
|
||||
|
||||
// Visit states with a compile-time filter (reduces template instantiations).
|
||||
template <template <typename> typename Predicate, visit_mode Mode, typename Visitor>
|
||||
template <visit_mode Mode, template <typename> typename... Predicates, typename Visitor>
|
||||
void visit_if(Visitor&& visitor) const
|
||||
{
|
||||
using state_visitor = state_visitor<const state_machine_base, Visitor, Mode, Predicate>;
|
||||
using state_visitor = state_visitor<const state_machine_base, Visitor, Mode, Predicates...>;
|
||||
state_visitor::visit(*this, std::forward<Visitor>(visitor));
|
||||
}
|
||||
|
||||
@@ -664,7 +621,7 @@ class state_machine_base : public FrontEnd
|
||||
m_optional_members.template get<context_member>() = &context;
|
||||
if constexpr (std::is_same_v<root_sm_t, no_root_sm>)
|
||||
{
|
||||
visit_if<is_back_end, visit_mode::all_recursive>(
|
||||
visit_if<visit_mode::all_recursive, has_state_machine_tag>(
|
||||
[&context](auto &state_machine)
|
||||
{
|
||||
state_machine.m_optional_members.template get<context_member>() = &context;
|
||||
@@ -753,14 +710,10 @@ class state_machine_base : public FrontEnd
|
||||
class is_state_active_helper
|
||||
{
|
||||
public:
|
||||
is_state_active_helper(const state_machine_base& sm) : m_sm(sm)
|
||||
{
|
||||
}
|
||||
|
||||
bool operator()() const
|
||||
static bool execute(const state_machine_base& sm)
|
||||
{
|
||||
bool found = false;
|
||||
m_sm.visit_if<found_or_back_end, visit_mode::active_recursive>(
|
||||
sm.visit_if<visit_mode::active_recursive, found_or_back_end>(
|
||||
[&found](const auto& state)
|
||||
{
|
||||
using StateToCheck = std::decay_t<decltype(state)>;
|
||||
@@ -775,9 +728,7 @@ class state_machine_base : public FrontEnd
|
||||
private:
|
||||
template <typename StateToCheck>
|
||||
using found_or_back_end = mp11::mp_or<std::is_same<State, StateToCheck>,
|
||||
is_back_end<StateToCheck>>;
|
||||
|
||||
const state_machine_base& m_sm;
|
||||
has_state_machine_tag<StateToCheck>>;
|
||||
};
|
||||
|
||||
public:
|
||||
@@ -785,7 +736,7 @@ class state_machine_base : public FrontEnd
|
||||
template <typename State>
|
||||
bool is_state_active() const
|
||||
{
|
||||
return is_state_active_helper<State>{*this}();
|
||||
return is_state_active_helper<State>::execute(*this);
|
||||
}
|
||||
|
||||
// Main function to process events.
|
||||
@@ -940,14 +891,18 @@ class state_machine_base : public FrontEnd
|
||||
template<typename State>
|
||||
static constexpr int get_state_id(const State&)
|
||||
{
|
||||
static_assert(mp11::mp_map_contains<state_map_mp11, State>::value);
|
||||
static_assert(
|
||||
mp11::mp_map_contains<state_map_mp11, State>::value,
|
||||
"The state must be contained in the state machine");
|
||||
return detail::get_state_id<state_map_mp11, State>::type::value;
|
||||
}
|
||||
// Return the id of a state in the sm.
|
||||
template<typename State>
|
||||
static constexpr int get_state_id()
|
||||
{
|
||||
static_assert(mp11::mp_map_contains<state_map_mp11, State>::value);
|
||||
static_assert(
|
||||
mp11::mp_map_contains<state_map_mp11, State>::value,
|
||||
"The state must be contained in the state machine");
|
||||
return detail::get_state_id<state_map_mp11, State>::type::value;
|
||||
}
|
||||
|
||||
@@ -1008,7 +963,7 @@ class state_machine_base : public FrontEnd
|
||||
template <visit_mode Mode, typename Visitor>
|
||||
void visit(Visitor&& visitor)
|
||||
{
|
||||
visit_if<always_true, Mode>(std::forward<Visitor>(visitor));
|
||||
visit_if<Mode>(std::forward<Visitor>(visitor));
|
||||
}
|
||||
|
||||
// Visit the states.
|
||||
@@ -1016,7 +971,7 @@ class state_machine_base : public FrontEnd
|
||||
template <visit_mode Mode, typename Visitor>
|
||||
void visit(Visitor&& visitor) const
|
||||
{
|
||||
visit_if<always_true, Mode>(std::forward<Visitor>(visitor));
|
||||
visit_if<Mode>(std::forward<Visitor>(visitor));
|
||||
}
|
||||
|
||||
// Puts the given event into the deferred events queue.
|
||||
@@ -1121,6 +1076,7 @@ class state_machine_base : public FrontEnd
|
||||
// handling of eventless transitions
|
||||
void try_process_completion_event(EventSource source, bool handled)
|
||||
{
|
||||
typedef typename generate_event_set<stt>::event_set_mp11 event_set_mp11;
|
||||
using first_completion_event = mp11::mp_find_if<event_set_mp11, has_completion_event>;
|
||||
// if none is found in the SM, nothing to do
|
||||
if constexpr (first_completion_event::value != mp11::mp_size<event_set_mp11>::value)
|
||||
@@ -1146,16 +1102,17 @@ class state_machine_base : public FrontEnd
|
||||
// Main function used internally to make transitions
|
||||
// Can only be called for internally (for example in an action method) generated events.
|
||||
template <class Event>
|
||||
process_result process_event_internal(Event const& event,
|
||||
process_result process_event_internal(
|
||||
Event const& event,
|
||||
EventSource source = EventSource::EVENT_SOURCE_DEFAULT)
|
||||
{
|
||||
// The compile policy decides whether the event needs to be wrapped or not.
|
||||
// After wrapping it should call back process_event_internal_impl.
|
||||
return compile_policy_impl::process_event_internal(*this, event, source);
|
||||
return process_event_internal_impl(
|
||||
compile_policy_impl::normalize_event(event), source);
|
||||
}
|
||||
|
||||
template <class Event>
|
||||
process_result process_event_internal_impl(Event const& event, EventSource source)
|
||||
process_result process_event_internal_impl(Event const& event,
|
||||
EventSource source)
|
||||
{
|
||||
// If the state machine has terminate or interrupt flags, check them.
|
||||
if constexpr (mp11::mp_any_of<state_set, is_state_blocking_t>::value)
|
||||
@@ -1184,9 +1141,11 @@ class state_machine_base : public FrontEnd
|
||||
}
|
||||
}
|
||||
|
||||
// If deferred events are configured and the event is to be deferred
|
||||
// in the active state configuration, then defer it for later processing.
|
||||
if constexpr (has_any_deferred_event::value)
|
||||
// If deferred events are configured and the event could be deferred
|
||||
// in the active state configuration, then conditionally defer it for later processing.
|
||||
if constexpr (has_deferring_states::value &&
|
||||
compile_policy_impl::template has_deferred_event<
|
||||
deferring_states, Event>::value)
|
||||
{
|
||||
if (compile_policy_impl::is_event_deferred(*this, event))
|
||||
{
|
||||
@@ -1197,11 +1156,11 @@ class state_machine_base : public FrontEnd
|
||||
|
||||
// Process the event.
|
||||
m_event_processing = true;
|
||||
process_result handled;
|
||||
process_result result;
|
||||
const bool is_direct_call = source & EventSource::EVENT_SOURCE_DIRECT;
|
||||
if constexpr (has_no_exception_thrown<front_end_t>::value)
|
||||
{
|
||||
handled = do_process_event(event, is_direct_call);
|
||||
result = do_process_event(event, is_direct_call);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1211,13 +1170,13 @@ class state_machine_base : public FrontEnd
|
||||
std::exception e;
|
||||
BOOST_TRY
|
||||
{
|
||||
handled = do_process_event(event, is_direct_call);
|
||||
result = do_process_event(event, is_direct_call);
|
||||
}
|
||||
BOOST_CATCH (std::exception& e)
|
||||
{
|
||||
// give a chance to the concrete state machine to handle
|
||||
this->exception_caught(event, get_fsm_argument(), e);
|
||||
handled = process_result::HANDLED_FALSE;
|
||||
result = process_result::HANDLED_FALSE;
|
||||
}
|
||||
BOOST_CATCH_END
|
||||
}
|
||||
@@ -1228,7 +1187,7 @@ class state_machine_base : public FrontEnd
|
||||
|
||||
// Process completion transitions BEFORE any other event in the
|
||||
// pool (UML Standard 2.3 15.3.14)
|
||||
try_process_completion_event(source, (handled & process_result::HANDLED_TRUE));
|
||||
try_process_completion_event(source, (result & process_result::HANDLED_TRUE));
|
||||
|
||||
// After handling, take care of the queued and deferred events.
|
||||
// Default:
|
||||
@@ -1261,7 +1220,7 @@ class state_machine_base : public FrontEnd
|
||||
}
|
||||
}
|
||||
|
||||
return handled;
|
||||
return result;
|
||||
}
|
||||
|
||||
// minimum event processing without exceptions, queues, etc.
|
||||
@@ -1276,22 +1235,16 @@ class state_machine_base : public FrontEnd
|
||||
}
|
||||
}
|
||||
|
||||
process_result handled = process_result::HANDLED_FALSE;
|
||||
process_result result = process_result::HANDLED_FALSE;
|
||||
// Dispatch the event to every region.
|
||||
for (int region_id = 0; region_id < nr_regions; region_id++)
|
||||
{
|
||||
handled = static_cast<process_result>(
|
||||
static_cast<int>(handled) |
|
||||
static_cast<int>(sm_dispatch_table::dispatch(*this, region_id, m_active_state_ids[region_id], event))
|
||||
);
|
||||
result |= sm_dispatch_table::dispatch(*this, m_active_state_ids[region_id], event);
|
||||
}
|
||||
// Process the event in the internal table of this fsm if the event is processable (present in the table).
|
||||
if constexpr (mp11::mp_set_contains<processable_events_internal_table,Event>::value)
|
||||
{
|
||||
handled = static_cast<process_result>(
|
||||
static_cast<int>(handled) |
|
||||
static_cast<int>(sm_dispatch_table::dispatch_internal(*this, 0, m_active_state_ids[0], event))
|
||||
);
|
||||
result |= sm_dispatch_table::dispatch_internal(*this, event);
|
||||
}
|
||||
|
||||
// if the event has not been handled and we have orthogonal zones, then
|
||||
@@ -1300,14 +1253,14 @@ class state_machine_base : public FrontEnd
|
||||
// but let the containing sm handle the error, unless the event was generated in this fsm
|
||||
// (by calling process_event on this fsm object, is_direct_call == true)
|
||||
// completion events do not produce an error
|
||||
if ((!is_contained() || is_direct_call) && !handled && !compile_policy_impl::is_completion_event(event))
|
||||
if ((!is_contained() || is_direct_call) && !result && !compile_policy_impl::is_completion_event(event))
|
||||
{
|
||||
for (const auto state_id: m_active_state_ids)
|
||||
{
|
||||
this->no_transition(event, get_fsm_argument(), state_id);
|
||||
}
|
||||
}
|
||||
return handled;
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -1390,7 +1343,7 @@ private:
|
||||
{
|
||||
// false or forward
|
||||
typedef typename ::boost::mpl::and_<
|
||||
typename has_back_end_tag<State>::type,
|
||||
typename has_state_machine_tag<State>::type,
|
||||
typename ::boost::mpl::not_<
|
||||
typename has_non_forwarding_flag<Flag>::type>::type >::type composite_no_forward;
|
||||
|
||||
@@ -1447,38 +1400,6 @@ private:
|
||||
try_process_completion_event(EventSource::EVENT_SOURCE_DEFAULT, true);
|
||||
}
|
||||
|
||||
// helper to find out if a SM has an active exit state and is therefore waiting for exiting
|
||||
template <class State, class StateOwner>
|
||||
inline
|
||||
bool is_exit_state_active()
|
||||
{
|
||||
if constexpr (has_pseudo_exit<State>::value)
|
||||
{
|
||||
typedef typename StateOwner::type Owner;
|
||||
Owner& owner = get_state<Owner&>();
|
||||
const int state_id = owner.template get_state_id<State>();
|
||||
for (const auto active_state_id : owner.get_active_state_ids())
|
||||
{
|
||||
if (active_state_id == state_id)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class State>
|
||||
struct find_region_id
|
||||
{
|
||||
template <int region,int Dummy=0>
|
||||
struct In
|
||||
{
|
||||
enum {region_index=region};
|
||||
};
|
||||
enum {region_index = In<State::zone_index>::region_index };
|
||||
};
|
||||
|
||||
// entry for states machines which are themselves embedded in other state machines (composites)
|
||||
template <class Event, class Fsm>
|
||||
void on_entry(Event const& event, Fsm& fsm)
|
||||
@@ -1503,7 +1424,7 @@ private:
|
||||
[this](auto state_identity)
|
||||
{
|
||||
using State = typename decltype(state_identity)::type::wrapped_entry;
|
||||
static constexpr int region_index = find_region_id<State>::region_index;
|
||||
static constexpr int region_index = State::zone_index;
|
||||
static_assert(region_index >= 0 && region_index < nr_regions);
|
||||
m_active_state_ids[region_index] = get_state_id<State>();
|
||||
}
|
||||
@@ -1555,11 +1476,11 @@ private:
|
||||
static void execute_entry(State& state, Event const& event, Fsm& fsm)
|
||||
{
|
||||
// calls on_entry on the fsm then handles direct entries, fork, entry pseudo state
|
||||
if constexpr (has_back_end_tag<State>::value)
|
||||
if constexpr (has_state_machine_tag<State>::value)
|
||||
{
|
||||
state.on_entry(event,fsm);
|
||||
}
|
||||
else if constexpr (has_pseudo_exit<State>::value)
|
||||
else if constexpr (has_exit_pseudostate_be_tag<State>::value)
|
||||
{
|
||||
// calls on_entry on the state then forward the event to the transition which should be defined inside the
|
||||
// contained fsm
|
||||
@@ -1596,7 +1517,7 @@ private:
|
||||
|
||||
template <typename State>
|
||||
using state_requires_init =
|
||||
mp11::mp_or<has_pseudo_exit<State>, is_back_end<State>>;
|
||||
mp11::mp_or<has_exit_pseudostate_be_tag<State>, has_state_machine_tag<State>>;
|
||||
|
||||
// initializes the SM
|
||||
template <class TRootSm>
|
||||
@@ -1615,12 +1536,12 @@ private:
|
||||
}
|
||||
m_root_sm = static_cast<void*>(&root_sm);
|
||||
|
||||
visit_if<state_requires_init, visit_mode::all_non_recursive>(
|
||||
visit_if<visit_mode::all_non_recursive, state_requires_init>(
|
||||
[&root_sm](auto& state)
|
||||
{
|
||||
using State = std::decay_t<decltype(state)>;
|
||||
|
||||
if constexpr (has_pseudo_exit<State>::value)
|
||||
if constexpr (has_exit_pseudostate_be_tag<State>::value)
|
||||
{
|
||||
state.set_forward_fct(
|
||||
[&root_sm](typename State::event const& event)
|
||||
@@ -1630,7 +1551,7 @@ private:
|
||||
);
|
||||
}
|
||||
|
||||
if constexpr (is_back_end<State>::value)
|
||||
if constexpr (has_state_machine_tag<State>::value)
|
||||
{
|
||||
static_assert(
|
||||
std::is_same_v<compile_policy,
|
||||
|
||||
@@ -19,12 +19,11 @@
|
||||
#include <boost/fusion/include/at_key.hpp>
|
||||
#include <boost/type_traits/add_const.hpp>
|
||||
|
||||
#include <boost/msm/front/detail/state_tags.hpp>
|
||||
|
||||
namespace boost { namespace msm { namespace front {namespace detail
|
||||
{
|
||||
|
||||
struct state_tag {};
|
||||
struct composite_state_tag {};
|
||||
|
||||
template <class Attributes= ::boost::fusion::map<> >
|
||||
struct inherit_attributes
|
||||
{
|
||||
|
||||
39
include/boost/msm/front/detail/state_tags.hpp
Normal file
39
include/boost/msm/front/detail/state_tags.hpp
Normal file
@@ -0,0 +1,39 @@
|
||||
// Copyright 2025 Christian Granzin
|
||||
// Copyright 2008 Christophe Henry
|
||||
// henry UNDERSCORE christophe AT hotmail DOT com
|
||||
// This is an extended version of the state machine available in the boost::mpl library
|
||||
// Distributed under the same license as the original.
|
||||
// Copyright for the original version:
|
||||
// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed
|
||||
// under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_MSM_FRONT_DETAIL_STATE_TAGS_HPP
|
||||
#define BOOST_MSM_FRONT_DETAIL_STATE_TAGS_HPP
|
||||
|
||||
#include <boost/type_traits/is_same.hpp>
|
||||
|
||||
namespace boost { namespace msm { namespace front { namespace detail
|
||||
{
|
||||
|
||||
// States
|
||||
struct state_tag {};
|
||||
struct composite_state_tag {};
|
||||
|
||||
// Pseudostates
|
||||
struct explicit_entry_tag {};
|
||||
template <typename T>
|
||||
using has_explicit_entry_tag = boost::is_same<typename T::internal::tag, explicit_entry_tag>;
|
||||
|
||||
struct entry_pseudostate_tag {};
|
||||
template <typename T>
|
||||
using has_entry_pseudostate_tag = boost::is_same<typename T::internal::tag, entry_pseudostate_tag>;
|
||||
|
||||
struct exit_pseudostate_tag {};
|
||||
template <typename T>
|
||||
using has_exit_pseudostate_tag = boost::is_same<typename T::internal::tag, exit_pseudostate_tag>;
|
||||
|
||||
}}}} // namespace boost::msm::front::detail
|
||||
|
||||
#endif // BOOST_MSM_FRONT_DETAIL_STATE_TAGS_HPP
|
||||
@@ -207,6 +207,8 @@ namespace boost { namespace msm { namespace front
|
||||
template <class EVENT,class ACTION=none,class GUARD=none>
|
||||
struct Internal
|
||||
{
|
||||
typedef none Source;
|
||||
typedef none Target;
|
||||
typedef EVENT Evt;
|
||||
typedef ACTION Action;
|
||||
typedef GUARD Guard;
|
||||
@@ -230,6 +232,8 @@ namespace boost { namespace msm { namespace front
|
||||
template<class EVENT,class ACTION>
|
||||
struct Internal<EVENT,ACTION,none>
|
||||
{
|
||||
typedef none Source;
|
||||
typedef none Target;
|
||||
typedef EVENT Evt;
|
||||
typedef ACTION Action;
|
||||
typedef none Guard;
|
||||
@@ -246,6 +250,8 @@ namespace boost { namespace msm { namespace front
|
||||
template<class EVENT,class GUARD>
|
||||
struct Internal<EVENT,none,GUARD>
|
||||
{
|
||||
typedef none Source;
|
||||
typedef none Target;
|
||||
typedef EVENT Evt;
|
||||
typedef none Action;
|
||||
typedef GUARD Guard;
|
||||
@@ -261,6 +267,8 @@ namespace boost { namespace msm { namespace front
|
||||
template<class EVENT>
|
||||
struct Internal<EVENT,none,none>
|
||||
{
|
||||
typedef none Source;
|
||||
typedef none Target;
|
||||
typedef EVENT Evt;
|
||||
typedef none Action;
|
||||
typedef none Guard;
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
|
||||
#include <boost/msm/front/common_states.hpp>
|
||||
#include <boost/msm/row_tags.hpp>
|
||||
//#include <boost/msm/back/metafunctions.hpp>
|
||||
|
||||
namespace boost { namespace msm { namespace front
|
||||
{
|
||||
@@ -145,6 +144,11 @@ struct entry_pseudo_state
|
||||
{
|
||||
// tags
|
||||
typedef int pseudo_entry;
|
||||
struct internal
|
||||
{
|
||||
typedef detail::entry_pseudostate_tag tag;
|
||||
};
|
||||
|
||||
enum {zone_index=ZoneIndex};
|
||||
typedef int explicit_entry_state;
|
||||
// default: no flag
|
||||
@@ -163,7 +167,12 @@ struct exit_pseudo_state : public boost::msm::front::detail::state_base<BASE> ,
|
||||
typedef Event event;
|
||||
typedef BASE Base;
|
||||
typedef SMPtrPolicy PtrPolicy;
|
||||
// tags
|
||||
typedef int pseudo_exit;
|
||||
struct internal
|
||||
{
|
||||
typedef detail::exit_pseudostate_tag tag;
|
||||
};
|
||||
|
||||
// default: no flag
|
||||
typedef ::boost::fusion::vector0<> flag_list;
|
||||
|
||||
@@ -60,7 +60,7 @@ using get_hierarchical_test_machines = boost::mpl::vector<
|
||||
>;
|
||||
|
||||
#define BOOST_MSM_TEST_DEFINE_DEPENDENT_TEMPLATES(frontname) \
|
||||
using base = msm::front::state_machine_def<frontname>; \
|
||||
using base = boost::msm::front::state_machine_def<frontname>; \
|
||||
template<typename T1, class Event, typename T2> \
|
||||
using _row = typename base::template _row<T1, Event, T2>; \
|
||||
template< \
|
||||
|
||||
@@ -145,7 +145,7 @@ struct serialize_state
|
||||
typename ::boost::enable_if<
|
||||
typename ::boost::mpl::or_<
|
||||
typename has_do_serialize<T>::type,
|
||||
typename detail::has_back_end_tag<typename T::internal>::type
|
||||
typename detail::has_state_machine_tag<typename T::internal>::type
|
||||
>::type
|
||||
,void
|
||||
>::type
|
||||
@@ -157,7 +157,7 @@ struct serialize_state
|
||||
typename ::boost::disable_if<
|
||||
typename ::boost::mpl::or_<
|
||||
typename has_do_serialize<T>::type,
|
||||
typename detail::has_back_end_tag<typename T::internal>::type
|
||||
typename detail::has_state_machine_tag<typename T::internal>::type
|
||||
>::type
|
||||
,void
|
||||
>::type
|
||||
|
||||
364
test/Backmp11EntryExit.cpp
Normal file
364
test/Backmp11EntryExit.cpp
Normal file
@@ -0,0 +1,364 @@
|
||||
// Copyright 2025 Christian Granzin
|
||||
// Copyright 2010 Christophe Henry
|
||||
// henry UNDERSCORE christophe AT hotmail DOT com
|
||||
// This is an extended version of the state machine available in the boost::mpl library
|
||||
// Distributed under the same license as the original.
|
||||
// Copyright for the original version:
|
||||
// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed
|
||||
// under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
// back-end
|
||||
#include "BackCommon.hpp"
|
||||
//front-end
|
||||
#include <boost/msm/front/state_machine_def.hpp>
|
||||
#include <boost/msm/front/functor_row.hpp>
|
||||
#ifndef BOOST_MSM_NONSTANDALONE_TEST
|
||||
#define BOOST_TEST_MODULE backmp11_entry_exit_test
|
||||
#endif
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
namespace mp11 = boost::mp11;
|
||||
using namespace boost::msm::front;
|
||||
using namespace boost::msm::backmp11;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
// events
|
||||
struct EnterSubmachine {};
|
||||
struct ExitSubmachine {};
|
||||
struct EnterSubmachineExplicitEntry {};
|
||||
struct EnterSubmachineForkEntry {};
|
||||
struct EnterSubmachinePseudoEntry {};
|
||||
struct ExitSubmachinePseudoExit {};
|
||||
struct SomeEvent
|
||||
{
|
||||
SomeEvent(){}
|
||||
template <class Event>
|
||||
SomeEvent(Event const&){}
|
||||
};
|
||||
|
||||
// states
|
||||
struct StateBase : state<>
|
||||
{
|
||||
template <class Event, class Fsm>
|
||||
void on_entry(const Event&, Fsm&)
|
||||
{
|
||||
entry_counter++;
|
||||
}
|
||||
|
||||
template <class Event, class Fsm>
|
||||
void on_exit(const Event&, Fsm&)
|
||||
{
|
||||
exit_counter++;
|
||||
}
|
||||
|
||||
size_t entry_counter{};
|
||||
size_t exit_counter{};
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct StateMachineBase_ : state_machine_def<T>
|
||||
{
|
||||
template <class Event, class Fsm>
|
||||
void on_entry(const Event&, Fsm&)
|
||||
{
|
||||
entry_counter++;
|
||||
}
|
||||
|
||||
template <class Event, class Fsm>
|
||||
void on_exit(const Event&, Fsm&)
|
||||
{
|
||||
exit_counter++;
|
||||
}
|
||||
|
||||
template <class FSM,class Event>
|
||||
void no_transition(Event const& , FSM&,int )
|
||||
{
|
||||
BOOST_FAIL("no_transition called!");
|
||||
}
|
||||
|
||||
size_t entry_counter{};
|
||||
size_t exit_counter{};
|
||||
};
|
||||
|
||||
template<typename Config = default_state_machine_config>
|
||||
struct hierarchical_state_machine
|
||||
{
|
||||
struct Submachine_ : StateMachineBase_<Submachine_>
|
||||
{
|
||||
BOOST_MSM_TEST_DEFINE_DEPENDENT_TEMPLATES(Submachine_)
|
||||
|
||||
struct Substate0 : StateBase {};
|
||||
struct Substate1 : StateBase {};
|
||||
struct ExplicitEntry0 : StateBase, explicit_entry<0> {};
|
||||
struct ExplicitEntry1 : StateBase, explicit_entry<1> {};
|
||||
struct Substate2 : StateBase {};
|
||||
|
||||
// TODO:
|
||||
// According to UML a pseudostate shouldn't have entry/exit behavior.
|
||||
struct PseudoEntry0 : entry_pseudo_state<0>
|
||||
{
|
||||
template <class Event, class Fsm>
|
||||
void on_entry(const Event&, Fsm&)
|
||||
{
|
||||
entry_counter++;
|
||||
}
|
||||
|
||||
template <class Event, class Fsm>
|
||||
void on_exit(const Event&, Fsm&)
|
||||
{
|
||||
exit_counter++;
|
||||
}
|
||||
|
||||
size_t entry_counter{};
|
||||
size_t exit_counter{};
|
||||
};
|
||||
|
||||
// TODO:
|
||||
// According to UML a pseudostate shouldn't have entry/exit behavior.
|
||||
struct PseudoExit0 : exit_pseudo_state<SomeEvent>
|
||||
{
|
||||
template <class Event, class Fsm>
|
||||
void on_entry(const Event&, Fsm&)
|
||||
{
|
||||
entry_counter++;
|
||||
}
|
||||
|
||||
template <class Event, class Fsm>
|
||||
void on_exit(const Event&, Fsm&)
|
||||
{
|
||||
exit_counter++;
|
||||
}
|
||||
|
||||
size_t entry_counter{};
|
||||
size_t exit_counter{};
|
||||
};
|
||||
|
||||
struct AssertEnterSubmachinePseudoEntry
|
||||
{
|
||||
template <typename Event, typename Fsm, typename Source, typename Target>
|
||||
void operator()(const Event&, Fsm&, Source&, Target&)
|
||||
{
|
||||
static_assert(std::is_same_v<Source, PseudoEntry0>);
|
||||
static_assert(std::is_same_v<Target, Substate2>);
|
||||
}
|
||||
};
|
||||
|
||||
struct AssertExitSubmachinePseudoExit1
|
||||
{
|
||||
template <typename Event, typename Fsm, typename Source, typename Target>
|
||||
void operator()(const Event&, Fsm&, Source&, Target&)
|
||||
{
|
||||
static_assert(std::is_same_v<Source, Substate2>);
|
||||
// TODO:
|
||||
// Should the action receive PseudoExit0 instead of Machine::exit_pt<PseudoExit0>?
|
||||
// static_assert(std::is_same_v<Target, PseudoExit0>);
|
||||
}
|
||||
};
|
||||
|
||||
using initial_state = mp11::mp_list<Substate0, Substate1>;
|
||||
using explicit_creation = mp11::mp_list<ExplicitEntry1>;
|
||||
|
||||
using transition_table = mp11::mp_list<
|
||||
Row < PseudoEntry0 , EnterSubmachinePseudoEntry , Substate2 , AssertEnterSubmachinePseudoEntry >,
|
||||
Row < ExplicitEntry0 , SomeEvent , Substate0 >,
|
||||
Row < Substate2 , ExitSubmachinePseudoExit , PseudoExit0 , AssertExitSubmachinePseudoExit1 >
|
||||
>;
|
||||
};
|
||||
|
||||
using Submachine = state_machine<Submachine_, Config>;
|
||||
|
||||
struct Machine_ : StateMachineBase_<Machine_>
|
||||
{
|
||||
BOOST_MSM_TEST_DEFINE_DEPENDENT_TEMPLATES(Machine_)
|
||||
|
||||
struct State0 : StateBase {};
|
||||
|
||||
using SubmachineEntryPt =
|
||||
typename Submachine::template entry_pt<typename Submachine_::PseudoEntry0>;
|
||||
using SubmachineExplicitEntryPt =
|
||||
typename Submachine::template direct<typename Submachine_::ExplicitEntry0>;
|
||||
using SubmachineForkEntryPt =
|
||||
mp11::mp_list<typename Submachine::template direct<typename Submachine_::ExplicitEntry0>,
|
||||
typename Submachine::template direct<typename Submachine_::ExplicitEntry1>>;
|
||||
using SubmachineExitPt =
|
||||
typename Submachine::template exit_pt<typename Submachine_::PseudoExit0>;
|
||||
|
||||
struct AssertEnterSubmachine
|
||||
{
|
||||
template <typename Event, typename Fsm, typename Source, typename Target>
|
||||
void operator()(const Event&, Fsm&, Source&, Target&)
|
||||
{
|
||||
static_assert(std::is_same_v<Source, State0>);
|
||||
static_assert(std::is_same_v<Target, Submachine>);
|
||||
}
|
||||
};
|
||||
|
||||
struct AssertEnterSubmachinePseudoEntry
|
||||
{
|
||||
template <typename Event, typename Fsm, typename Source, typename Target>
|
||||
void operator()(const Event&, Fsm&, Source&, Target&)
|
||||
{
|
||||
static_assert(std::is_same_v<Source, State0>);
|
||||
// TODO:
|
||||
// Should be PseudoEntry0 instead of Submachine.
|
||||
static_assert(std::is_same_v<Target, Submachine>);
|
||||
}
|
||||
};
|
||||
|
||||
struct AssertEnterSubmachineExplicitEntry
|
||||
{
|
||||
template <typename Event, typename Fsm, typename Source, typename Target>
|
||||
void operator()(const Event&, Fsm&, Source&, Target&)
|
||||
{
|
||||
static_assert(std::is_same_v<Source, State0>);
|
||||
// TODO:
|
||||
// Should be ExplicitEntry0 instead of Submachine.
|
||||
// static_assert(std::is_same_v<Target, typename Submachine_::ExplicitEntry0>);
|
||||
static_assert(std::is_same_v<Target, Submachine>);
|
||||
}
|
||||
};
|
||||
|
||||
struct AssertEnterSubmachineForkEntry
|
||||
{
|
||||
template <typename Event, typename Fsm, typename Source, typename Target>
|
||||
void operator()(const Event&, Fsm&, Source&, Target&)
|
||||
{
|
||||
static_assert(std::is_same_v<Source, State0>);
|
||||
// TODO:
|
||||
// Should be mp11::mp_list<ExplicitEntry0, ExplicitEntry1> instead of Submachine (??).
|
||||
static_assert(std::is_same_v<Target, Submachine>);
|
||||
}
|
||||
};
|
||||
|
||||
using initial_state = State0;
|
||||
|
||||
using transition_table = mp11::mp_list<
|
||||
Row < State0 , EnterSubmachine , Submachine , AssertEnterSubmachine >,
|
||||
Row < State0 , EnterSubmachinePseudoEntry , SubmachineEntryPt , AssertEnterSubmachinePseudoEntry >,
|
||||
Row < State0 , EnterSubmachineExplicitEntry , SubmachineExplicitEntryPt , AssertEnterSubmachineExplicitEntry >,
|
||||
Row < State0 , EnterSubmachineForkEntry , SubmachineForkEntryPt , AssertEnterSubmachineForkEntry>,
|
||||
Row < Submachine , ExitSubmachine , State0 >,
|
||||
Row < SubmachineExitPt , SomeEvent , State0 >
|
||||
>;
|
||||
};
|
||||
|
||||
using Machine = state_machine<Machine_, Config>;
|
||||
};
|
||||
|
||||
using TestMachines = mp11::mp_list<
|
||||
hierarchical_state_machine<>,
|
||||
hierarchical_state_machine<favor_compile_time_config>
|
||||
>;
|
||||
|
||||
#define CHECK_AND_RESET_COUNTER(counter, expected) \
|
||||
{ \
|
||||
BOOST_REQUIRE(counter == expected); \
|
||||
counter = 0; \
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE_TEMPLATE(backmp11_entry_exit_test, test_machine, TestMachines)
|
||||
{
|
||||
using Machine = typename test_machine::Machine;
|
||||
using Machine_ = typename test_machine::Machine_;
|
||||
Machine p;
|
||||
|
||||
using State0 = typename Machine_::State0;
|
||||
|
||||
using Submachine = typename test_machine::Submachine;
|
||||
using Substate0 = typename Submachine::Substate0;
|
||||
using Substate1 = typename Submachine::Substate1;
|
||||
using Substate2 = typename Submachine::Substate2;
|
||||
using PseudoEntry0 = typename Submachine::PseudoEntry0;
|
||||
using ExplicitEntry0 = typename Submachine::ExplicitEntry0;
|
||||
using ExplicitEntry1 = typename Submachine::ExplicitEntry1;
|
||||
// TODO:
|
||||
// Can we define PseudoExit0 as seen from the submachine?
|
||||
using PseudoExit0 = typename Machine_::SubmachineExitPt;
|
||||
|
||||
|
||||
auto& state_0 = p.template get_state<State0>();
|
||||
|
||||
auto& submachine = p.template get_state<Submachine&>();
|
||||
auto& substate_0 = submachine.template get_state<Substate0>();
|
||||
auto& substate_1 = submachine.template get_state<Substate1>();
|
||||
auto& substate_2 = submachine.template get_state<Substate2>();
|
||||
auto& pseudo_entry_0 = submachine.template get_state<PseudoEntry0>();
|
||||
auto& explicit_entry_0 = submachine.template get_state<ExplicitEntry0>();
|
||||
auto& explicit_entry_1 = submachine.template get_state<ExplicitEntry1>();
|
||||
auto& pseudo_exit_0 = submachine.template get_state<PseudoExit0>();
|
||||
|
||||
p.start();
|
||||
BOOST_REQUIRE(p.template is_state_active<State0>());
|
||||
CHECK_AND_RESET_COUNTER(state_0.entry_counter, 1);
|
||||
|
||||
// Normal entry/exit: [Substate0, Substate1].
|
||||
p.process_event(EnterSubmachine{});
|
||||
BOOST_REQUIRE(p.template is_state_active<Submachine>());
|
||||
BOOST_REQUIRE(p.template is_state_active<Substate0>());
|
||||
BOOST_REQUIRE(p.template is_state_active<Substate1>());
|
||||
CHECK_AND_RESET_COUNTER(state_0.exit_counter, 1);
|
||||
CHECK_AND_RESET_COUNTER(submachine.entry_counter, 1);
|
||||
CHECK_AND_RESET_COUNTER(substate_0.entry_counter, 1);
|
||||
CHECK_AND_RESET_COUNTER(substate_1.entry_counter, 1);
|
||||
p.process_event(ExitSubmachine{});
|
||||
BOOST_REQUIRE(p.template is_state_active<State0>());
|
||||
CHECK_AND_RESET_COUNTER(state_0.entry_counter, 1);
|
||||
CHECK_AND_RESET_COUNTER(submachine.exit_counter, 1);
|
||||
CHECK_AND_RESET_COUNTER(substate_0.exit_counter, 1);
|
||||
CHECK_AND_RESET_COUNTER(substate_1.exit_counter, 1);
|
||||
|
||||
// Pseudo entry: [Substate1, Substate2] via PseudoEntry0.
|
||||
p.process_event(EnterSubmachinePseudoEntry{});
|
||||
BOOST_REQUIRE(p.template is_state_active<Substate1>());
|
||||
BOOST_REQUIRE(p.template is_state_active<Substate2>());
|
||||
CHECK_AND_RESET_COUNTER(state_0.exit_counter, 1);
|
||||
CHECK_AND_RESET_COUNTER(submachine.entry_counter, 1);
|
||||
CHECK_AND_RESET_COUNTER(pseudo_entry_0.entry_counter, 1);
|
||||
CHECK_AND_RESET_COUNTER(pseudo_entry_0.exit_counter, 1);
|
||||
CHECK_AND_RESET_COUNTER(substate_2.entry_counter, 1);
|
||||
CHECK_AND_RESET_COUNTER(substate_1.entry_counter, 1);
|
||||
|
||||
// Pseudo exit: Transitions via PseudoExit0.
|
||||
p.process_event(ExitSubmachinePseudoExit{});
|
||||
BOOST_REQUIRE(p.template is_state_active<State0>());
|
||||
CHECK_AND_RESET_COUNTER(pseudo_exit_0.entry_counter, 1);
|
||||
CHECK_AND_RESET_COUNTER(pseudo_exit_0.exit_counter, 1);
|
||||
CHECK_AND_RESET_COUNTER(submachine.exit_counter, 1);
|
||||
CHECK_AND_RESET_COUNTER(substate_1.exit_counter, 1);
|
||||
CHECK_AND_RESET_COUNTER(substate_2.exit_counter, 1);
|
||||
CHECK_AND_RESET_COUNTER(state_0.entry_counter, 1);
|
||||
|
||||
// Explicit entry: [ExplicitEntry0, SubState1].
|
||||
p.process_event(EnterSubmachineExplicitEntry{});
|
||||
BOOST_REQUIRE(p.template is_state_active<ExplicitEntry0>());
|
||||
BOOST_REQUIRE(p.template is_state_active<Substate1>());
|
||||
CHECK_AND_RESET_COUNTER(state_0.exit_counter, 1);
|
||||
CHECK_AND_RESET_COUNTER(submachine.entry_counter, 1);
|
||||
CHECK_AND_RESET_COUNTER(explicit_entry_0.entry_counter, 1);
|
||||
CHECK_AND_RESET_COUNTER(substate_1.entry_counter, 1);
|
||||
p.process_event(ExitSubmachine{});
|
||||
CHECK_AND_RESET_COUNTER(state_0.entry_counter, 1);
|
||||
CHECK_AND_RESET_COUNTER(submachine.exit_counter, 1);
|
||||
CHECK_AND_RESET_COUNTER(explicit_entry_0.exit_counter, 1);
|
||||
CHECK_AND_RESET_COUNTER(substate_1.exit_counter, 1);
|
||||
|
||||
// Explicit entry with fork: [ExplicitEntry0, ExplicitEntry1].
|
||||
p.process_event(EnterSubmachineForkEntry{});
|
||||
BOOST_REQUIRE(p.template is_state_active<ExplicitEntry0>());
|
||||
BOOST_REQUIRE(p.template is_state_active<ExplicitEntry1>());
|
||||
CHECK_AND_RESET_COUNTER(state_0.exit_counter, 1);
|
||||
CHECK_AND_RESET_COUNTER(submachine.entry_counter, 1);
|
||||
CHECK_AND_RESET_COUNTER(explicit_entry_0.entry_counter, 1);
|
||||
CHECK_AND_RESET_COUNTER(explicit_entry_1.entry_counter, 1);
|
||||
p.process_event(ExitSubmachine{});
|
||||
CHECK_AND_RESET_COUNTER(state_0.entry_counter, 1);
|
||||
CHECK_AND_RESET_COUNTER(submachine.exit_counter, 1);
|
||||
CHECK_AND_RESET_COUNTER(explicit_entry_0.exit_counter, 1);
|
||||
CHECK_AND_RESET_COUNTER(explicit_entry_1.exit_counter, 1);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -34,6 +34,7 @@ namespace
|
||||
|
||||
// Events.
|
||||
struct MyEvent{};
|
||||
struct MyInternalEvent{};
|
||||
|
||||
// Actions
|
||||
struct MyAction
|
||||
@@ -81,7 +82,8 @@ struct StateMachine_ : public state_machine_def<StateMachine_>
|
||||
|
||||
using initial_state = Default;
|
||||
using transition_table = mp11::mp_list<
|
||||
Row<Default, MyEvent, none, MyAction, MyGuard>
|
||||
Row<Default, MyEvent, none, MyAction, MyGuard>,
|
||||
Internal<MyInternalEvent, MyAction, MyGuard>
|
||||
>;
|
||||
};
|
||||
// Leave this class without inheriting constructors to check
|
||||
|
||||
@@ -75,6 +75,7 @@ add_executable(boost_msm_cxx17_tests
|
||||
Backmp11Constructor.cpp
|
||||
Backmp11Context.cpp
|
||||
Backmp11Deferred.cpp
|
||||
Backmp11EntryExit.cpp
|
||||
Backmp11ManyDeferTransitions.cpp
|
||||
Backmp11Members.cpp
|
||||
Backmp11RootSm.cpp
|
||||
|
||||
@@ -79,6 +79,7 @@ test-suite msm-unit-tests-cxxstd17
|
||||
[ run Backmp11Constructor.cpp ]
|
||||
[ run Backmp11Context.cpp ]
|
||||
[ run Backmp11Deferred.cpp ]
|
||||
[ run Backmp11EntryExit.cpp ]
|
||||
[ run Backmp11ManyDeferTransitions.cpp ]
|
||||
[ run Backmp11Members.cpp ]
|
||||
[ run Backmp11Visitor.cpp ]
|
||||
|
||||
Reference in New Issue
Block a user