// 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_VISITOR_HPP #define BOOST_MSM_BACKMP11_DETAIL_STATE_VISITOR_HPP #include #include namespace boost::msm::backmp11::detail { constexpr bool has_flag(visit_mode value, visit_mode flag) { return (static_cast(value) & static_cast(flag)) != 0; } // Helper to filter with multiple predicates consecutively. // Uses template unrolling to enable re-usage of template instances. template typename... Predicates> struct copy_if_impl; template typename Predicate, template typename... Predicates> struct copy_if_impl { using subset = mp11::mp_copy_if; using type = typename copy_if_impl::type; }; template struct copy_if_impl { using type = List; }; template typename... Predicates> using copy_if = typename copy_if_impl::type; template typename... Predicates> class state_visitor_base_impl; template typename... Predicates> class state_visitor_base_impl< StateMachine, /*Recursive=*/false, Predicates...> { public: using states_to_visit = copy_if; using states_to_traverse = states_to_visit; using needs_traversal = mp11::mp_not>; template static void accept(StateMachine& sm, Visitor& visitor) { auto& state = sm.template get_state(); visitor(state); } }; // Set with traversal info for recursive visits. // Uses template unrolling to enable re-usage of template instances. template typename... Predicates> struct recursive_visit_set; template struct recursive_visit_set { using states_to_visit = typename StateMachine::internal::state_set; using submachines_to_traverse = typename StateMachine::internal::submachines; using states_to_traverse = states_to_visit; using needs_traversal = mp11::mp_true; }; template typename Predicate> struct recursive_visit_set { using states_to_visit = copy_if; template using submachine_needs_traversal = typename recursive_visit_set< Submachine, Predicate>::needs_traversal; using submachines_to_traverse = mp11::mp_copy_if; using states_to_traverse = mp11::mp_set_union; using needs_traversal = mp11::mp_not>; }; template typename FirstPredicate, template typename Predicate> struct recursive_visit_set : recursive_visit_set { using base = recursive_visit_set; using states_to_visit = copy_if; template using submachine_needs_traversal = typename recursive_visit_set< Submachine, Predicate>::needs_traversal; using submachines_to_traverse = mp11::mp_copy_if; using states_to_traverse = mp11::mp_set_union; using needs_traversal = mp11::mp_not>; }; template typename... Predicates> class state_visitor_base_impl< StateMachine, /*Recursive=*/true, Predicates...> { using visit_set = recursive_visit_set; public: using needs_traversal = typename visit_set::needs_traversal; using states_to_traverse = typename visit_set::states_to_traverse; template static void accept(StateMachine& sm, Visitor& visitor) { auto& state = sm.template get_state(); if constexpr (mp11::mp_contains::value) { visitor(state); } if constexpr (mp11::mp_contains< typename visit_set::submachines_to_traverse, State>::value) { state.template visit_if(visitor); } } }; template typename... Predicates> using state_visitor_base = state_visitor_base_impl; template < typename StateMachine, typename Visitor, visit_mode Mode, bool AllStates, template typename... Predicates> class state_visitor_impl; template < typename StateMachine, typename Visitor, visit_mode Mode, template typename... Predicates> class state_visitor_impl< StateMachine, Visitor, Mode, /*AllStates=*/false, Predicates...> : public state_visitor_base { using base = state_visitor_base; public: static void visit(StateMachine& sm, Visitor& visitor) { if constexpr (base::needs_traversal::value) { if (sm.m_running) { using state_identities = mp11::mp_transform< mp11::mp_identity, typename base::states_to_traverse>; for (const int active_state_id : sm.m_active_state_ids) { mp11::mp_for_each( [&sm, &visitor, active_state_id](auto state_identity) { using State = typename decltype(state_identity)::type; constexpr int state_id = StateMachine::template get_state_id(); if (active_state_id == state_id) { base::template accept(sm, visitor); } }); } } } } }; template < typename StateMachine, typename Visitor, visit_mode Mode, template typename... Predicates> class state_visitor_impl< StateMachine, Visitor, Mode, /*AllStates=*/true, Predicates...> : public state_visitor_base { using base = state_visitor_base; public: static void visit(StateMachine& sm, Visitor& visitor) { if constexpr (base::needs_traversal::value) { using state_identities = mp11::mp_transform< mp11::mp_identity, typename base::states_to_traverse>; mp11::mp_for_each( [&sm, &visitor](auto state_identity) { using State = typename decltype(state_identity)::type; base::template accept(sm, visitor); } ); } } }; // State visitor. // 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... Predicates> using state_visitor = state_visitor_impl< StateMachine, Visitor, Mode, has_flag(Mode, visit_mode::all_states), Predicates...>; // Custom visitor tailored to event deferral. // We need an active_recursive implementation with the ability // to additionally pass the Fsm parameter. template < typename StateMachine, typename Visitor, template typename... Predicates> class event_deferral_visitor { using visit_set = recursive_visit_set; public: using needs_traversal = typename visit_set::needs_traversal; static void visit(StateMachine& sm, Visitor& visitor) { // Ensure the necessity to instantiate this function // is evaluated before it's called. static_assert(visit_set::needs_traversal::value, "The visitor must have at least one state to visit"); if (sm.m_running) { using state_identities = mp11::mp_transform< mp11::mp_identity, typename visit_set::states_to_traverse>; for (const int active_state_id : sm.m_active_state_ids) { mp11::mp_for_each( [&sm, &visitor, active_state_id](auto state_identity) { using State = typename decltype(state_identity)::type; constexpr int state_id = StateMachine::template get_state_id(); if (active_state_id == state_id) { accept(sm, visitor); } }); } } } private: template static void accept(StateMachine& sm, Visitor& visitor) { auto& state = sm.template get_state(); if constexpr (mp11::mp_contains::value) { visitor(state, sm.get_fsm_argument()); } if constexpr (mp11::mp_contains::value) { using submachine_visitor = event_deferral_visitor; submachine_visitor::visit(state, visitor); } } }; // Predefined visitor functors used in backmp11. class is_event_deferred_visitor_base { public: // Apply (at least) a pre-filter with the 'has_deferred_events' predicate // to consider only deferring states, this subset is cheap to instantiate. // Compile policies can decide to apply additional filters. template using predicate = has_deferred_events; bool result() const { return m_result; } protected: bool m_result{false}; }; template class is_state_active_visitor { public: template using predicate = std::is_same; void operator()(const State&) { m_result = true; } bool result() const { return m_result; } private: bool m_result{false}; }; template class is_flag_active_visitor; template class is_flag_active_visitor { public: template using predicate = mp11::mp_contains, Flag>; template void operator()(const State&) { m_result = true; } bool result() const { return m_result; } private: bool m_result{false}; }; template class is_flag_active_visitor { public: template using predicate = mp11::mp_not, Flag>>; template void operator()(const State&) { m_result = false; } bool result() const { return m_result; } private: bool m_result{true}; }; template class init_state_visitor { public: template using predicate = mp11::mp_or, has_state_machine_tag>; init_state_visitor(RootSm& root_sm) : m_root_sm(root_sm) { } template void operator()(State& state) { if constexpr (has_exit_pseudostate_be_tag::value) { state.template init(); } if constexpr (has_state_machine_tag::value) { static_assert( std::is_same_v || std::is_same_v, "The configured root_sm must match the used one"); static_assert( std::is_same_v || std::is_same_v, "The configured context must match the root sm's one"); static_assert(std::is_same_v, "All compile policies must be identical"); *state.m_root_sm = &m_root_sm; } } private: RootSm& m_root_sm; }; } // boost::msm::backmp11::detail #endif // BOOST_MSM_BACKMP11_DETAIL_STATE_VISITOR_HPP