diff --git a/include/boost/msm/backmp11/detail/metafunctions.hpp b/include/boost/msm/backmp11/detail/metafunctions.hpp index f7af4e99..2845637f 100644 --- a/include/boost/msm/backmp11/detail/metafunctions.hpp +++ b/include/boost/msm/backmp11/detail/metafunctions.hpp @@ -35,6 +35,9 @@ 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; diff --git a/include/boost/msm/backmp11/detail/state_visitor.hpp b/include/boost/msm/backmp11/detail/state_visitor.hpp new file mode 100644 index 00000000..3dc928cb --- /dev/null +++ b/include/boost/msm/backmp11/detail/state_visitor.hpp @@ -0,0 +1,153 @@ +// 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 + +namespace boost::msm::backmp11::detail +{ + +// Helper to provide a zero-cost abstraction if Predicate is always_true. +template typename Predicate> +struct copy_if_impl +{ + using type = mp11::mp_copy_if; +}; +template +struct copy_if_impl +{ + using type = List; +}; +template typename Predicate> +using copy_if = typename copy_if_impl::type; + +// State visitor implementation. +// States to visit can be selected with VisitMode +// and additionally be compile-time filtered with a predicate. +template < + typename StateMachine, + typename Visitor, + visit_mode Mode, + template typename Predicate = always_true, + typename Enable = void> +class state_visitor; + +template < + typename StateMachine, + typename Visitor, + visit_mode Mode, + template typename Predicate> +class state_visitor< + StateMachine, + Visitor, Mode, + Predicate, + std::enable_if_t> +{ + using state_set = typename StateMachine::internal::state_set; + using state_subset = copy_if; + using state_identities = mp11::mp_transform; + public: + state_visitor() + { + mp11::mp_for_each( + [this](auto state_identity) + { + using State = typename decltype(state_identity)::type; + m_cells[StateMachine::template get_state_id()] = &accept; + } + ); + } + + static void visit(StateMachine& sm, Visitor visitor) + { + if (sm.m_running) + { + const state_visitor& self = instance(); + for (const int state_id : sm.m_active_state_ids) + { + self.dispatch(sm, state_id, std::forward(visitor)); + } + } + } + + private: + using cell_t = void (*)(StateMachine&, Visitor); + + template + static void accept(StateMachine& sm, Visitor visitor) + { + auto& state = sm.template get_state(); + visitor(state); + + if constexpr (has_back_end_tag::value && + has_flag(Mode, visit_mode::recursive)) + { + state.template visit_if(std::forward(visitor)); + } + } + + static const state_visitor& instance() + { + static const state_visitor instance; + return instance; + } + + void dispatch(StateMachine& sm, int state_id, Visitor visitor) const + { + cell_t cell = m_cells[state_id]; + if (cell) + { + (*cell)(sm, std::forward(visitor)); + } + } + + cell_t m_cells[mp11::mp_size::value]{}; +}; + +template < + typename StateMachine, + typename Visitor, + visit_mode Mode, + template typename Predicate> +class state_visitor< + StateMachine, + Visitor, Mode, + Predicate, + std::enable_if_t> +{ + using state_set = typename StateMachine::internal::state_set; + using state_subset = copy_if; + using state_identities = mp11::mp_transform; + public: + static void visit(StateMachine& sm, Visitor visitor) + { + mp11::mp_for_each( + [&sm, &visitor](auto state_identity) + { + using State = typename decltype(state_identity)::type; + auto& state = sm.template get_state(); + visitor(state); + + if constexpr (is_back_end::value && + has_flag(Mode, visit_mode::recursive)) + { + state.template visit_if(std::forward(visitor)); + } + } + ); + } +}; + +} // boost::msm::backmp11::detail + +#endif // BOOST_MSM_BACKMP11_DETAIL_STATE_VISITOR_HPP diff --git a/include/boost/msm/backmp11/state_machine.hpp b/include/boost/msm/backmp11/state_machine.hpp index f67b16a3..d5d55349 100644 --- a/include/boost/msm/backmp11/state_machine.hpp +++ b/include/boost/msm/backmp11/state_machine.hpp @@ -38,9 +38,10 @@ #include #include -#include #include +#include #include +#include #include namespace boost { namespace msm { namespace backmp11 @@ -52,12 +53,6 @@ using detail::is_composite; namespace detail { -constexpr bool is_valid(visit_mode mode) { - constexpr uint8_t state_mask = 0b011; - const uint8_t state_bits = static_cast(mode) & state_mask; - return state_bits == 0b001 || state_bits == 0b010; -} - template < class FrontEnd, class Config, @@ -441,6 +436,9 @@ class state_machine_base : public FrontEnd template friend struct detail::compile_policy_impl; + template typename, typename> + friend class state_visitor; + // Allow access to private members for serialization. // WARNING: // No guarantee is given on the private member layout. @@ -629,25 +627,23 @@ class state_machine_base : public FrontEnd template bool is_event_deferred(const Event& event) const { - return compile_policy_impl::is_event_deferred( - *const_cast(this), event); + return compile_policy_impl::is_event_deferred(*this, event); } // Visit states with a compile-time filter (reduces template instantiations). template