// 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_METAFUNCTIONS_HPP #define BOOST_MSM_BACKMP11_DETAIL_METAFUNCTIONS_HPP #include #include #include #include #include #include #include #include // Forward declarations to support MPL->Mp11 conversions // without MPL header dependencies. namespace boost::mpl { template struct copy; template struct back_inserter; template struct is_sequence; } namespace boost::msm::backmp11::detail { // Call a functor on all elements of List, until the functor returns true. // Performs short-circuit evaluation at runtime. template struct mp_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. template struct optional_instance; template struct optional_instance { using type = T; type instance; static constexpr bool value = true; }; template struct optional_instance { using type = T; static constexpr bool value = false; }; // Helper to convert a single type or MPL sequence to Mp11 template struct to_mp_list { typedef typename mpl::copy>>::type type; }; template struct to_mp_list::value>> { using type = mp11::mp_list; }; template struct to_mp_list> { typedef mp11::mp_list type; }; template using to_mp_list_t = typename to_mp_list::type; // Convert a list with integral constants of the same value_type to a value array. template struct value_array_impl; template struct value_array_impl> { using value_type = typename mp11::mp_front>::value_type; static constexpr value_type value[sizeof...(Ts)] {Ts::value...}; }; template static constexpr const auto& value_array = value_array_impl::value; // 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; }; 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 { // 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_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_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 struct generate_state_map_impl { using indices = mp11::mp_iota>; using type = mp11::mp_transform_q< mp11::mp_bind, StateSet, indices>; }; template using generate_state_map = typename generate_state_map_impl::type; // returns the id of a given state template struct get_state_id_impl { using type = mp11::mp_second>; }; template using get_state_id = typename get_state_id_impl::type; // iterates through the transition table and generate a set containing all the events template struct generate_event_set_impl { template using set_push_event = mp11::mp_set_push_back; using type = mp11::mp_fold< to_mp_list_t, mp11::mp_list<>, set_push_event>; }; template using generate_event_set = typename generate_event_set_impl::type; template using has_deferred_events = mp11::mp_not>>; template using has_deferred_event = mp11::mp_contains, Event>; // Builds flags from flag_list + internal_flag_list, // internal_flag_list is used for terminate/interrupt states. template using get_flag_list = mp11::mp_append< to_mp_list_t, to_mp_list_t>; template struct is_state_blocking_impl { template using has_event_blocking_flag = typename has_event_blocking_flag::type; typedef typename mp11::mp_any_of< get_flag_list, has_event_blocking_flag > type; }; template using is_state_blocking = typename is_state_blocking_impl::type; } // boost::msm::backmp11::detail #endif // BOOST_MSM_BACKMP11_DETAIL_METAFUNCTIONS_HPP