2
0
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:
Christian Granzin
2026-01-01 11:45:28 -05:00
committed by Christian Granzin
parent 155b3ef6d6
commit 128228e667
19 changed files with 1274 additions and 782 deletions

View File

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

View File

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

View File

@@ -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];
};
};

View File

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

View 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

View File

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

View 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

View File

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

View File

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

View File

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

View 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -75,6 +75,7 @@ add_executable(boost_msm_cxx17_tests
Backmp11Constructor.cpp
Backmp11Context.cpp
Backmp11Deferred.cpp
Backmp11EntryExit.cpp
Backmp11ManyDeferTransitions.cpp
Backmp11Members.cpp
Backmp11RootSm.cpp

View File

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