From c7f75714da6444150593cf5062adc47a7a320c2b Mon Sep 17 00:00:00 2001 From: Christian Granzin Date: Thu, 1 Jan 2026 11:45:28 -0500 Subject: [PATCH] refactor(backmp11): various topics --- .../pages/tutorial/backmp11-back-end.adoc | 14 +- include/boost/msm/backmp11/common_types.hpp | 42 +- .../backmp11/detail/favor_runtime_speed.hpp | 424 ++++++++---------- .../msm/backmp11/detail/metafunctions.hpp | 413 ++++++++--------- .../boost/msm/backmp11/detail/state_tags.hpp | 47 ++ .../msm/backmp11/detail/state_visitor.hpp | 83 ++-- .../msm/backmp11/detail/transition_table.hpp | 86 ++++ .../boost/msm/backmp11/favor_compile_time.hpp | 203 +++++---- include/boost/msm/backmp11/state_machine.hpp | 283 +++++------- .../boost/msm/front/detail/common_states.hpp | 5 +- include/boost/msm/front/detail/state_tags.hpp | 39 ++ include/boost/msm/front/functor_row.hpp | 8 + include/boost/msm/front/states.hpp | 11 +- test/BackCommon.hpp | 2 +- test/Backmp11Adapter.hpp | 6 +- test/Backmp11EntryExit.cpp | 364 +++++++++++++++ test/Backmp11Members.cpp | 4 +- test/CMakeLists.txt | 1 + test/Jamfile.v2 | 1 + 19 files changed, 1264 insertions(+), 772 deletions(-) create mode 100644 include/boost/msm/backmp11/detail/state_tags.hpp create mode 100644 include/boost/msm/backmp11/detail/transition_table.hpp create mode 100644 include/boost/msm/front/detail/state_tags.hpp create mode 100644 test/Backmp11EntryExit.cpp diff --git a/doc/modules/ROOT/pages/tutorial/backmp11-back-end.adoc b/doc/modules/ROOT/pages/tutorial/backmp11-back-end.adoc index 00cca454..fea8fbf7 100644 --- a/doc/modules/ROOT/pages/tutorial/backmp11-back-end.adoc +++ b/doc/modules/ROOT/pages/tutorial/backmp11-back-end.adoc @@ -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 |================================================================================ diff --git a/include/boost/msm/backmp11/common_types.hpp b/include/boost/msm/backmp11/common_types.hpp index 62629079..e7e59dd7 100644 --- a/include/boost/msm/backmp11/common_types.hpp +++ b/include/boost/msm/backmp11/common_types.hpp @@ -16,7 +16,7 @@ #include #include -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( + static_cast>(lhs) | + static_cast>(rhs) + ); +} + +constexpr HandledEnum& operator|=(HandledEnum& lhs, HandledEnum rhs) +{ + lhs = lhs | rhs; + return lhs; +} + +constexpr HandledEnum operator&(HandledEnum lhs, HandledEnum rhs) +{ + return static_cast( + static_cast>(lhs) & + static_cast>(rhs) + ); +} + +constexpr HandledEnum& operator&=(HandledEnum& lhs, HandledEnum rhs) +{ + lhs = lhs & rhs; + return lhs; +} + +} // namespace boost::msm::back #endif // BOOST_MSM_BACKMP11_COMMON_TYPES_H diff --git a/include/boost/msm/backmp11/detail/favor_runtime_speed.hpp b/include/boost/msm/backmp11/detail/favor_runtime_speed.hpp index 98a601a1..b5e73846 100644 --- a/include/boost/msm/backmp11/detail/favor_runtime_speed.hpp +++ b/include/boost/msm/backmp11/detail/favor_runtime_speed.hpp @@ -16,19 +16,6 @@ #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - namespace boost { namespace msm { namespace backmp11 { @@ -44,6 +31,10 @@ struct compile_policy_impl { 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 static constexpr bool is_completion_event(const Event&) { @@ -56,10 +47,10 @@ struct compile_policy_impl return sm.template is_flag_active>(); } - template - static process_result process_event_internal(StateMachine& sm, const Event& event, EventSource source) + template + constexpr static const Event& normalize_event(const Event& event) { - return sm.process_event_internal_impl(event, source); + return event; } template @@ -73,8 +64,7 @@ struct compile_policy_impl 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 Event m_event; }; - template - using has_deferred_event = mp11::mp_contains< - to_mp_list_t, - Event - >; + template + using has_deferred_event = has_deferred_event; template class is_event_deferred_helper @@ -106,14 +93,17 @@ struct compile_policy_impl { result = true; }; - sm.template visit_if(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(visitor); return result; } private: + using deferring_states = typename StateMachine::deferring_states; template - using defers_event = has_deferred_event; + using has_deferred_event = has_deferred_event; }; template @@ -128,9 +118,9 @@ struct compile_policy_impl { if constexpr (is_kleene_event::value) { - using event_list = typename StateMachine::event_set_mp11; + typedef typename generate_event_set::event_set_mp11 event_list; bool found = - for_each_until>( + mp_for_each_until>( [&sm, &event](auto event_identity) { using KnownEvent = typename decltype(event_identity)::type; @@ -177,49 +167,53 @@ struct compile_policy_impl } }; - template - struct table_index + template> + struct get_table_index_impl; + template + struct get_table_index_impl { - using type = mp11::mp_if< - mp11::mp_not>, - mp11::mp_size_t() + 1>, - mp11::mp_size_t<0> - >; + using type = mp11::mp_size_t() + 1>; }; - template - using get_table_index = typename table_index::type; + template + struct get_table_index_impl + { + using type = mp11::mp_size_t<0>; + }; + template + using get_table_index = typename get_table_index_impl::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 + template class dispatch_table { - using Stt = typename Fsm::complete_table; + using Stt = typename StateMachine::complete_table; public: // Dispatch function for a specific event. template - using cell = HandledEnum (*)(Fsm&, int,int,Event const&); + using cell = process_result (*)(StateMachine&, int&, Event const&); // Dispatch an event. template - 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::instance().entries[state_id+1](fsm, region_id, state_id, event); + return event_dispatch_table::instance().entries[state_id+1](sm, state_id, event); } // Dispatch an event to the FSM's internal table. template - 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::instance().entries[0](fsm, region_id, state_id, event); + int no_state_id; + return event_dispatch_table::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::state_set state_set; - BOOST_STATIC_CONSTANT(int, max_state = (mp11::mp_size::value)); + // to make the tables. + using state_set = typename StateMachine::internal::state_set; + static constexpr int max_state = mp11::mp_size::value; // Dispatch table for a specific event. template @@ -235,39 +229,12 @@ struct compile_policy_impl } 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 ::boost::disable_if::type, void>::type - operator()(Row) - { - m_entries[get_table_index::value] = - &Row::execute; - } - - template - typename ::boost::enable_if::type, void>::type - operator()(Row) - { - m_entries[get_table_index::value] = - &convert_event_and_forward::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 > 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 get_init_cells(), mp11::mp_size::value ); -#else - mp11::mp_for_each(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 + 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 - static - HandledEnum - execute(Fsm& , int, int, Event const& , ::boost::mpl::true_ const & ) - { - // if at least one guard rejected, this will be ignored, otherwise will generate an error - return HandledEnum::HANDLED_FALSE; - } - - template - 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::type first_row; - HandledEnum res = first_row::execute(fsm,region_index,state,evt); - if (HandledEnum::HANDLED_TRUE!=res && HandledEnum::HANDLED_DEFERRED!=res) + process_result result = process_result::HANDLED_FALSE; + mp_for_each_until( + [&result, &sm, &state_id, &evt](auto transition) { - // if the first rejected, move on to the next one - HandledEnum sub_res = - execute::type>(fsm,region_index,state,evt, - ::boost::mpl::bool_< - ::boost::mpl::empty::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; + 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; + } + return false; } - return res; - } - }; - // Take the transition action and return the next state. - static HandledEnum execute(Fsm& fsm, int region_index, int state, Event const& evt) - { - // forward to helper - return execute_helper::template execute(fsm,region_index,state,evt, - ::boost::mpl::bool_< ::boost::mpl::empty::type::value>()); + ); + return result; } }; - // nullary metafunction whose only job is to prevent early evaluation of _1 - template< typename Entry > - struct make_chain_row_from_map_entry - { - // 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::value }; - //erases the first NumberToDelete rows - template - struct erase_first_rows - { - typedef typename ::boost::mpl::erase< - typename Entry::second, - typename ::boost::mpl::begin::type, - typename ::boost::mpl::advance< - typename ::boost::mpl::begin::type, - ::boost::mpl::int_ >::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, - ::boost::mpl::identity - >::type filtered_stt; - - typedef chain_row type; - }; - // helper for lazy evaluation in eval_if of change_frow_event - template - struct replace_event + template < + typename State, + typename FilteredTransitionTable, + bool MoreThanOneFrow = (mp11::mp_count_if::value > 1)> + struct make_transition_chain_impl; + template + struct make_transition_chain_impl { - typedef typename Transition::template replace_event::type type; + using type = transition_chain; }; - // 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 - struct change_frow_event + template + struct make_transition_chain_impl { - typedef typename ::boost::mp11::mp_if_c< - has_is_frow::type::value, - replace_event, - boost::mp11::mp_identity - >::type type; + // if we have more than one frow with the same state as source, remove the ones extra + // 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::value; + using type = + transition_chain>; }; + template + using make_transition_chain = typename make_transition_chain_impl::type; - template - struct convert_event_and_forward + template + static process_result convert_event_and_execute(StateMachine& sm, int& state_id, Event const& evt) { - 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; @@ -433,69 +344,124 @@ struct compile_policy_impl // Helpers for row processing // First operation (fold) + template ::value> + struct event_filter_predicate_impl + { + using type = std::is_base_of; + }; template - using event_filter_predicate = mp11::mp_and< - mp11::mp_not>, - mp11::mp_or< - std::is_base_of, - typename is_kleene_event::type - > - >; + struct event_filter_predicate_impl + { + using type = mp11::mp_true; + }; + template + using event_filter_predicate = + typename event_filter_predicate_impl::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 ::value> + struct normalize_transition_impl; + template + struct normalize_transition_impl + { + using type = Transition; + }; + template + struct normalize_transition_impl + { + using type = typename Transition::template replace_event; + }; + template + using normalize_transition = typename normalize_transition_impl::type; + template using push_map_value = mp11::mp_push_front< mp11::mp_second>, Value>; - template - 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::value, - // first row on this source state, make a list with 1 element - mp11::mp_list::type>, - // list already exists, add the row - push_map_value, - M, - typename T::current_state_type, - typename change_frow_event::type + template::value> + struct map_updater_impl; + template + struct map_updater_impl + { + using type = mp11::mp_map_replace< + Map, + // list already exists, add the row + mp11::mp_list< + typename Transition::current_state_type, + push_map_value< + Map, + typename Transition::current_state_type, + normalize_transition > > >; - // Second operation (transform) - template - using to_mpl_map_entry = mpl::pair< - mp11::mp_first, - mp11::mp_second + }; + template + struct map_updater_impl + { + 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> + > >; - template - using row_chainer = mp11::mp_if_c< - (mp11::mp_size>>::value > 1), - // we need row chaining - typename make_chain_row_from_map_entry>::type, + }; + template + using map_updater = typename map_updater_impl::type; + + // Second operation (transform) + template< + typename StateAndFilteredTransitionTable, + bool MultipleTransitions = (mp11::mp_size>::value > 1)> + struct transition_chainer_impl; + template + struct transition_chainer_impl + { // just one row, no chaining, we rebuild the row like it was before - mp11::mp_front> - >; - template - using preprocess_row_helper = cell_constant<&Row::execute>; - template - using preprocess_row = init_cell_constant< - // Offset into the entries array - get_table_index::value, - // Address of the execute function - mp11::mp_eval_if_c< - is_kleene_event::type::value, - cell_constant< - &convert_event_and_forward::execute - >, - preprocess_row_helper, - Row - >::value - >; + using type = mp11::mp_front>; + }; + template + struct transition_chainer_impl + { + // we need row chaining + using type = make_transition_chain< + mp11::mp_first, + mp11::mp_second>; + }; + template + using transition_chainer = typename transition_chainer_impl::type; + template::value> + struct preprocess_transition_impl; + template + struct preprocess_transition_impl + { + using type = init_cell_constant< + // Offset into the entries array + get_table_index::value, + // Address of the execute function + cell_constant<&Transition::execute>::value + >; + }; + template + struct preprocess_transition_impl + { + using type = init_cell_constant< + // Offset into the entries array + get_table_index::value, + // Address of the execute function + cell_constant<&convert_event_and_execute>::value + >; + }; + template + using preprocess_transition = typename preprocess_transition_impl::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]; }; }; diff --git a/include/boost/msm/backmp11/detail/metafunctions.hpp b/include/boost/msm/backmp11/detail/metafunctions.hpp index 7447f6fe..2a3dd00f 100644 --- a/include/boost/msm/backmp11/detail/metafunctions.hpp +++ b/include/boost/msm/backmp11/detail/metafunctions.hpp @@ -17,17 +17,13 @@ #include #include -#include - -#include -#include - -#include #include +#include #include -#include - +#include +#include +#include namespace boost { namespace msm { namespace backmp11 { @@ -35,44 +31,25 @@ namespace boost { namespace msm { namespace backmp11 namespace detail { -template -using always_true = mp11::mp_true; - constexpr bool has_flag(visit_mode value, visit_mode flag) { return (static_cast(value) & static_cast(flag)) != 0; } -struct back_end_tag {}; - -template -using has_back_end_tag = std::is_same; - -template -using is_back_end = has_back_end_tag; - -template -using is_composite = mp11::mp_or< - std::is_same, - has_back_end_tag - >; - // Call a functor on all elements of List, until the functor returns true. -template -constexpr bool for_each_until(Func&& func) -{ - bool condition = false; - - boost::mp11::mp_for_each( - [&func, &condition](auto&& item) - { - if (!condition) - { - condition = func(std::forward(item)); - } - } - ); - return condition; +template +struct for_each_until_impl +{ + template + static constexpr bool invoke(F &&func) + { + return (... || func(Ts{})); + } +}; +template +constexpr bool mp_for_each_until(F &&func) +{ + return mp11::mp_apply::invoke(std::forward(func)); } // Wrapper for an instance of a type, which might not be present. @@ -111,29 +88,165 @@ struct to_mp_list> template using to_mp_list_t = typename to_mp_list::type; -template -struct generate_state_set; +// Helper to convert a front-end state to a back-end state. +template +struct convert_state_impl +{ + using type = State; +}; +// Specialization for a 'direct' state wrapper struct used as target state (defined in the back-end). +template +struct convert_state_impl::value>> +{ + using type = typename State::owner; +}; +// Specialization for a "direct fork", a sequence of 'direct' state wrappers used directly as the target state. +template +struct convert_state_impl::value>> +{ + using target_states = to_mp_list_t; + using type = typename mp11::mp_front::owner; +}; +// Specialization for a 'entry_pt' state wrapper struct (defined in the back-end). +template +struct convert_state_impl::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 +struct convert_state_impl::value>> +{ + using type = typename StateMachine::template exit_pt; +}; +// Specialization for a 'exit_pt' struct (defined in the back-end). +template +struct convert_state_impl::value>> +{ + using type = typename State::owner; +}; -// 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 -struct generate_state_set +template +struct convert_source_state_impl : convert_state_impl {}; +template +struct convert_source_state_impl< + StateMachine, + State, + std::enable_if_t::value>> + : convert_state_impl +{ + // 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::value, + "'exit_pseudo_state' is only allowed as target state"); +}; +template +struct convert_source_state_impl< + StateMachine, + State, + std::enable_if_t::value>> + : convert_state_impl +{ + // Explicit entries can only denote targets. + static_assert(!has_explicit_entry_be_tag::value, + "'direct' is only allowed as target state"); +}; +template +struct convert_source_state_impl< + StateMachine, + State, + std::enable_if_t::value>> + : convert_state_impl { - typedef to_mp_list_t stt; - // first add the source states + // Explicit entries can only denote targets. + static_assert(!mpl::is_sequence::value, + "'fork' is only allowed as target state"); +}; +template +using convert_source_state = typename convert_source_state_impl::type; + +template +struct convert_target_state_impl : convert_state_impl {}; +template +struct convert_target_state_impl< + StateMachine, + State, + std::enable_if_t::value>> + : convert_state_impl +{ + // 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::value, + "'exit_pt' is only allowed as source state"); +}; +template +struct convert_target_state_impl< + StateMachine, + State, + std::enable_if_t::value>> + : convert_state_impl +{ + static_assert(!front::detail::has_entry_pseudostate_tag::value, + "'entry_pseudo_state' is only allowed as source state"); +}; +template +using convert_target_state = typename convert_target_state_impl::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 +struct generate_state_set_impl +{ + using front_end_t = typename StateMachine::front_end_t; + using transition_table = to_mp_list_t; + + // First add the source states. template - using set_push_source_state = - mp11::mp_set_push_back; - using source_state_set = - mp11::mp_fold, set_push_source_state>; - // then add the target states + using set_push_source_state = mp11::mp_if_c< + !std::is_same_v, + mp11::mp_set_push_back>, + V>; + using partial_state_set_0 = + mp11::mp_fold, set_push_source_state>; + + // Then add the target states. template - using set_push_target_state = - mp11::mp_set_push_back; - using state_set = - mp11::mp_fold; + using set_push_target_state = mp11::mp_if_c< + !std::is_same_v, + mp11::mp_set_push_back>, + V>; + using partial_state_set_1 = + mp11::mp_fold; + + // Then add the initial states. + using initial_states = to_mp_list_t; + static_assert(mp11::mp_is_set::value, "Each initial state must be unique"); + using partial_state_set_2 = mp11::mp_set_union; + + // Then add the states marked for explicit creation. + template + using add_explicit_creation_states = + mp11::mp_set_union>; + using type = mp11::mp_eval_if_c< + !has_explicit_creation::value, + partial_state_set_2, + add_explicit_creation_states, + front_end_t + >; }; +template +using generate_state_set = typename generate_state_set_impl::type; // extends a state set to a map with key=state and value=id template @@ -201,148 +314,45 @@ struct get_event_id enum {value = type::value}; }; -template -using has_deferred_events = mp11::mp_not< - mp11::mp_empty> - >; - -template -using has_deferred_event = mp11::mp_contains< - to_mp_list_t, - Event - >; - -// Template used to create dummy entries for initial states not found in the stt. -template< typename T1 > -struct not_a_row +template +struct has_deferred_events_impl { - typedef int not_real_row_tag; - struct dummy_event - { - }; - typedef T1 current_state_type; - typedef T1 next_state_type; - typedef dummy_event transition_event; + using type = mp11::mp_not< + mp11::mp_empty> + >; }; - -// 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 -struct get_wrapped_state +template +struct has_deferred_events_impl> { - template - using get_wrapped_entry = typename T::wrapped_entry; - using type = mp11::mp_eval_or; + using states = mp11::mp_list; + template + using has_deferred_events = typename has_deferred_events_impl::type; + + using type = mp11::mp_any_of; }; +template +using has_deferred_events = typename has_deferred_events_impl::type; -// returns the transition table of a Composite state -template -struct get_transition_table +template +struct has_deferred_event_impl { - typedef typename Derived::internal::template create_real_stt::type Stt; - // get the state set - typedef typename generate_state_set::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 - using states_pusher = mp11::mp_if_c< - mp11::mp_set_contains::value, - V, - mp11::mp_push_back< - V, - not_a_row::type> - > - >; - typedef typename mp11::mp_fold< - to_mp_list_t, - to_mp_list_t, - states_pusher - > with_init; - - // do the same for states marked as explicitly created - template - using get_explicit_creation = to_mp_list_t; - 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 - using convert_fake_state = mp11::mp_if_c< - has_direct_entry::value, - typename Derived::template direct, - State - >; - using explicit_created = mp11::mp_transform< - convert_fake_state, - fake_explicit_created - >; - - typedef typename mp11::mp_fold< - to_mp_list_t, - with_init, - states_pusher - > type; -}; -template -using get_transition_table_t = typename get_transition_table::type; - -// recursively builds an internal table including those of substates, sub-substates etc. -// variant for submachines -template -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 - using append_recursive_internal_transition_table = mp11::mp_append< - V, - typename recursive_get_internal_transition_table::value>::type + using type = mp11::mp_contains< + to_mp_list_t, + Event >; - typedef typename mp11::mp_fold< - composite_states, - to_mp_list_t, - append_recursive_internal_transition_table - > type; }; -// stop iterating on leafs (simple states) -template -struct recursive_get_internal_transition_table +template +struct has_deferred_event_impl, Event> { - 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 -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::value, - mp11::mp_list<>, - get_transition_table_t, - Composite - > org_table; + using states = mp11::mp_list; + template + using has_deferred_event = typename has_deferred_event_impl::type; + using subset = mp11::mp_copy_if; - typedef typename generate_state_set::state_set states; - - // and for every substate, recursively get the transition table if it's a state machine - template - using append_recursive_transition_table = mp11::mp_append< - V, - typename recursive_get_transition_table::type - >; - typedef typename mp11::mp_fold< - states, - org_table, - append_recursive_transition_table> type; + using type = mp11::mp_any_of; }; +template +using has_deferred_event = typename has_deferred_event_impl::type; // event used internally for wrapping a direct entry template @@ -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 -struct get_owner -{ - using type = mp11::mp_if< - mp11::mp_same, - State, - typename State::owner - >; -}; - - -template -struct get_fork_owner -{ - typedef typename ::boost::mpl::front::type seq_front; - typedef typename ::boost::mpl::if_< - typename ::boost::mpl::not_< - typename ::boost::is_same::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 struct get_flag_list diff --git a/include/boost/msm/backmp11/detail/state_tags.hpp b/include/boost/msm/backmp11/detail/state_tags.hpp new file mode 100644 index 00000000..9a8edea3 --- /dev/null +++ b/include/boost/msm/backmp11/detail/state_tags.hpp @@ -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 + +#include + +namespace boost::msm::backmp11::detail +{ + +// States +struct state_machine_tag {}; +template +using has_state_machine_tag = std::is_same; +template +using is_composite = mp11::mp_or< + std::is_same, + has_state_machine_tag + >; + +// Pseudostates +struct explicit_entry_be_tag {}; +template +using has_explicit_entry_be_tag = std::is_same; + +struct entry_pseudostate_be_tag {}; +template +using has_entry_pseudostate_be_tag = std::is_same; + +struct exit_pseudostate_be_tag {}; +template +using has_exit_pseudostate_be_tag = std::is_same; + +} // namespace boost::msm::backmp11::detail + +#endif // BOOST_MSM_BACKMP11_DETAIL_STATE_TAGS_HPP diff --git a/include/boost/msm/backmp11/detail/state_visitor.hpp b/include/boost/msm/backmp11/detail/state_visitor.hpp index e93ed5d6..ee33840b 100644 --- a/include/boost/msm/backmp11/detail/state_visitor.hpp +++ b/include/boost/msm/backmp11/detail/state_visitor.hpp @@ -17,54 +17,58 @@ namespace boost::msm::backmp11::detail { -// Helper to provide a zero-cost abstraction if Predicate is always_true. -template typename Predicate> -struct copy_if_impl +// Helper to apply multiple predicates sequentially. +template typename... Predicates> +struct copy_if_impl; +template typename Predicate, template typename... Predicates> +struct copy_if_impl { - using type = mp11::mp_copy_if; + using subset = mp11::mp_copy_if; + using type = typename copy_if_impl::type; }; template -struct copy_if_impl +struct copy_if_impl { using type = List; }; -template typename Predicate> -using copy_if = typename copy_if_impl::type; +template typename... Predicates> +using copy_if = typename copy_if_impl::type; -template typename Predicate> -using state_subset = copy_if; +template typename... Predicates> +using state_subset = copy_if; // 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 Predicate = always_true, - typename Enable = void> -class state_visitor; + typename Enable = void, + template typename... Predicates + > +class state_visitor_impl; template < typename StateMachine, typename Visitor, visit_mode Mode, - template typename Predicate> -class state_visitor< + template typename... Predicates> +class state_visitor_impl< StateMachine, Visitor, Mode, - Predicate, std::enable_if_t< has_flag(Mode, visit_mode::active_states) && - mp11::mp_not>>::value>> + mp11::mp_not>>::value>, + Predicates...> { public: - state_visitor() + state_visitor_impl() { using state_identities = mp11::mp_transform>; + state_subset>; mp11::mp_for_each( [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)); @@ -96,16 +100,16 @@ class state_visitor< auto& state = sm.template get_state(); visitor(state); - if constexpr (has_back_end_tag::value && + if constexpr (has_state_machine_tag::value && has_flag(Mode, visit_mode::recursive)) { - state.template visit_if(std::forward(visitor)); + state.template visit_if(std::forward(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 Predicate> -class state_visitor< + template typename... Predicates> +class state_visitor_impl< StateMachine, Visitor, Mode, - Predicate, std::enable_if_t< has_flag(Mode, visit_mode::all_states) && - mp11::mp_not>>::value>> + mp11::mp_not>>::value>, + Predicates...> { public: static void visit(StateMachine& sm, Visitor visitor) { using state_identities = mp11::mp_transform>; + state_subset>; mp11::mp_for_each( [&sm, &visitor](auto state_identity) { @@ -148,10 +152,10 @@ class state_visitor< auto& state = sm.template get_state(); visitor(state); - if constexpr (is_back_end::value && + if constexpr (has_state_machine_tag::value && has_flag(Mode, visit_mode::recursive)) { - state.template visit_if(std::forward(visitor)); + state.template visit_if(std::forward(visitor)); } } ); @@ -162,14 +166,14 @@ template < typename StateMachine, typename Visitor, visit_mode Mode, - template typename Predicate> -class state_visitor< + template typename... Predicates> +class state_visitor_impl< StateMachine, Visitor, Mode, - Predicate, std::enable_if_t< - mp11::mp_empty>::value>> + mp11::mp_empty>::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... Predicates> +using state_visitor = state_visitor_impl; + } // boost::msm::backmp11::detail #endif // BOOST_MSM_BACKMP11_DETAIL_STATE_VISITOR_HPP diff --git a/include/boost/msm/backmp11/detail/transition_table.hpp b/include/boost/msm/backmp11/detail/transition_table.hpp new file mode 100644 index 00000000..b8f90f95 --- /dev/null +++ b/include/boost/msm/backmp11/detail/transition_table.hpp @@ -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 + +namespace boost::msm::backmp11::detail +{ + +// returns the transition table of a Composite state +template +struct get_transition_table +{ + using Stt = typename Derived::internal::template create_real_stt::type; + using type = Stt; +}; +template +using get_transition_table_t = typename get_transition_table::type; + +// recursively builds an internal table including those of substates, sub-substates etc. +// variant for submachines +template +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 + using append_recursive_internal_transition_table = mp11::mp_append< + V, + typename recursive_get_internal_transition_table::value>::type + >; + typedef typename mp11::mp_fold< + composite_states, + to_mp_list_t, + append_recursive_internal_transition_table + > type; +}; +// stop iterating on leafs (simple states) +template +struct recursive_get_internal_transition_table +{ + 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 +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::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, has_state_machine_tag>; + template + using append_recursive_transition_table = mp11::mp_append< + V, + typename recursive_get_transition_table::type + >; + typedef typename mp11::mp_fold< + submachines, + org_table, + append_recursive_transition_table> type; +}; + +} + +#endif // BOOST_MSM_BACKMP11_TRANSITION_TABLE_HPP diff --git a/include/boost/msm/backmp11/favor_compile_time.hpp b/include/boost/msm/backmp11/favor_compile_time.hpp index fdf5bef4..0a04a22e 100644 --- a/include/boost/msm/backmp11/favor_compile_time.hpp +++ b/include/boost/msm/backmp11/favor_compile_time.hpp @@ -50,6 +50,10 @@ struct compile_policy_impl { 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 return helper.is_end_interrupt_event(event); } - template - static HandledEnum process_event_internal(StateMachine& sm, const Event& event, EventSource source) + template + static any_event normalize_event(const Event& event) { - return sm.process_event_internal_impl(any_event(event), source); + return any_event{event}; } - template - 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 @@ -84,8 +88,7 @@ struct compile_policy_impl 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 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 + using has_deferred_event = has_deferred_events; + template static const std::unordered_set& get_deferred_event_type_indices() { @@ -123,15 +131,16 @@ struct compile_policy_impl template 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; const auto& set = get_deferred_event_type_indices(); result |= (set.find(type_index) != set.end()); }; - sm.template visit_if(visitor); + sm.template visit_if(visitor); return result; } @@ -143,14 +152,6 @@ struct compile_policy_impl new deferred_event_impl(sm, event, deferred_events.cur_seq_cnt)}); } - template - struct get_real_rows - { - template - using is_real_row = mp11::mp_not::type>; - typedef mp11::mp_copy_if type; - }; - // Convert an event to a type index. template static std::type_index to_type_index() @@ -165,7 +166,8 @@ struct compile_policy_impl template end_interrupt_event_helper(const StateMachine& sm) { - mp11::mp_for_each>( + typedef typename generate_event_set::event_set_mp11 event_set_mp11; + mp11::mp_for_each>( [this, &sm](auto event_identity) { using Event = typename decltype(event_identity)::type; @@ -190,136 +192,151 @@ struct compile_policy_impl 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 - HandledEnum operator()(Fsm& fsm, int region, int state, any_event const& evt) const + public: + template + 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::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(*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(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; + // At this point result can be HANDLED_FALSE or HANDLED_GUARD_REJECT. + return result; } - // Use a deque with a generic type to avoid unnecessary template instantiations. - std::deque one_state; + + template + void add_transition() + { + m_transition_functions.emplace_front(reinterpret_cast( + &convert_event_and_execute)); + } + + private: + // Adapter for calling a transition's execute function. + template + 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)); + } + + std::deque 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 + template 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 - 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)); - } + 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 - void init_call_submachine() + // Initialize the call to the composite state's process_event function. + template + void init_composite_state() { - m_call_submachine = [](Fsm& fsm, const any_event& evt) - { - return (fsm.template get_state()).process_event_internal(evt); - }; + m_call_process_event = &call_process_event; } - template - chain_row& get_chain_row() + // Add a transition to the dispatch table. + template + void add_transition() { - return m_entries[to_type_index()]; + transition_chain& chain = m_transition_chains[to_type_index()]; + chain.add_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 m_entries; - // Special functor if the state is a composite - std::function m_call_submachine; + template + static process_result call_process_event(StateMachine& sm, const any_event& event) + { + return sm.template get_state().process_event_internal(event); + } + + std::unordered_map 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::type>( - [this](auto row) + mp11::mp_for_each( + [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(); - auto& chain_row = m_state_dispatch_tables[state_id + 1].template get_chain_row(); - chain_row.one_state.push_front(reinterpret_cast(&convert_and_execute)); + 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(); + m_state_dispatch_tables[state_id + 1].template add_transition(); }); // Execute state-specific initializations. - using submachine_states = mp11::mp_copy_if; + using submachine_states = mp11::mp_copy_if; mp11::mp_for_each>( [this](auto state_identity) { - using SubmachineState = typename decltype(state_identity)::type; - static constexpr int state_id = Fsm::template get_state_id(); - m_state_dispatch_tables[state_id + 1].template init_call_submachine(); + using CompositeState = typename decltype(state_identity)::type; + static constexpr int state_id = StateMachine::template get_state_id(); + m_state_dispatch_tables[state_id + 1].template init_composite_state(); }); } // 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::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::value)); state_dispatch_table m_state_dispatch_tables[max_state+1]; }; @@ -327,9 +344,9 @@ struct compile_policy_impl #ifndef BOOST_MSM_BACKMP11_MANUAL_GENERATION -template -const typename compile_policy_impl::template dispatch_table& -compile_policy_impl::dispatch_table::instance() +template +const typename compile_policy_impl::template dispatch_table& +compile_policy_impl::dispatch_table::instance() { static dispatch_table table; return table; diff --git a/include/boost/msm/backmp11/state_machine.hpp b/include/boost/msm/backmp11/state_machine.hpp index 65b3cf39..180ddf57 100644 --- a/include/boost/msm/backmp11/state_machine.hpp +++ b/include/boost/msm/backmp11/state_machine.hpp @@ -41,6 +41,7 @@ #include #include #include +#include #include #include @@ -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; static constexpr int nr_regions = mp11::mp_size::value; - template - struct make_entry - { - using type = State; - }; - template - struct make_entry::value>> - { - using type = entry_pt; - }; - template - struct make_entry::value>> - { - using type = direct; - }; - - template - struct make_exit - { - using type = State; - }; - template - struct make_exit::value>> - { - using type = exit_pt; - }; - - template 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 struct Transition { - typedef typename Row::Evt transition_event; - typedef typename make_entry::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::type, - get_owner, - ::boost::mpl::identity >::type current_state_type; - - typedef typename make_exit::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::type, - get_fork_owner, - ::boost::mpl::eval_if< - typename has_no_automatic_create::type, - get_owner, - ::boost::mpl::identity > - >::type next_state_type; + using transition_event = typename Row::Evt; + using current_state_type = convert_source_state; + using next_state_type = convert_target_state; // 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())); - BOOST_STATIC_CONSTANT(int, next_state = (get_state_id())); - 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::type::value && - !sm.is_exit_state_active>()) - { - return process_result::HANDLED_FALSE; - } + static constexpr int current_state_id = get_state_id(); + static constexpr int next_state_id = get_state_id(); + BOOST_ASSERT(state_id == current_state_id); auto& source = sm.get_state(); auto& target = sm.get_state(); @@ -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(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(target,event,sm); - sm.m_active_state_ids[region_index] = active_state_switching::after_entry(current_state,next_state); + convert_event_and_execute_entry(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())); - 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()); auto& source = sm.get_state(); auto& target = source; @@ -401,8 +358,8 @@ class state_machine_base : public FrontEnd > type; }; + using state_set = generate_state_set; using stt = typename get_transition_table::type; - using state_set = typename generate_state_set::state_set; }; typedef mp11::mp_rename states_t; @@ -436,8 +393,8 @@ class state_machine_base : public FrontEnd template friend struct detail::compile_policy_impl; - template typename, typename> - friend class state_visitor; + template 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()).process_event_internal(event); - sm.m_active_state_ids[region_index]=get_state_id(); - return res; + process_result result = + sm.get_state().process_event_internal(event); + state_id = get_state_id(); + 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 - struct replace_event - { - typedef frow type; - }; + using replace_event = frow; }; template @@ -530,7 +484,9 @@ class state_machine_base : public FrontEnd }; typedef typename generate_state_map::type state_map_mp11; - typedef typename generate_event_set::event_set_mp11 event_set_mp11; + // TODO: + // Recursion somewhere, can't define this in the SM (yet). + // typedef typename generate_event_set::event_set_mp11 event_set_mp11; typedef history_impl concrete_history; typedef typename generate_event_set< typename create_real_stt::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; + using deferring_states = mp11::mp_copy_if; + using has_deferring_states = + mp11::mp_not>; using deferred_events_member = optional_instance::value>; using events_queue_member = optional_instance 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 typename... Predicates, typename Visitor> void visit_if(Visitor&& visitor) { - using state_visitor = state_visitor; + using state_visitor = state_visitor; state_visitor::visit(*this, std::forward(visitor)); } - - // Visit states with a compile-time filter (reduces template instantiations). - template