// 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_TRANSITION_TABLE_HPP #define BOOST_MSM_BACKMP11_DETAIL_TRANSITION_TABLE_HPP #include "boost/assert.hpp" #include #include #include "boost/msm/backmp11/state_machine_config.hpp" namespace boost::msm::front { struct Defer; }; namespace boost::msm::backmp11::detail { // Chain of priority tags for SFINAE handling: // priority_tag_0 // ↓ (SFINAE fails?) // priority_tag_1 (base of priority_tag_0) // ↓ (SFINAE fails?) // priority_tag_2 (base of priority_tag_1) struct priority_tag_2 {}; struct priority_tag_1 : priority_tag_2 {}; struct priority_tag_0 : priority_tag_1 {}; template auto invoke_functor(priority_tag_0, Functor&&, const Event& event, Fsm& fsm, Source& source, Target& target) -> decltype(Functor{}(event, fsm, source, target)) { return Functor{}(event, fsm, source, target); } template auto invoke_functor(priority_tag_1, Functor&&, const Event& event, Fsm& fsm, Source&, Target&) -> decltype(Functor{}(event, fsm)) { return Functor{}(event, fsm); } template auto invoke_functor(priority_tag_2, Functor&&, const Event&, Fsm& fsm, Source&, Target&) -> decltype(Functor{}(fsm)) { return Functor{}(fsm); } template using get_Guard = typename Row::Guard; template struct has_Guard : mp11::mp_valid {}; template struct invoke_guard_functor { template static bool execute(const Event& event, Fsm& fsm, Source& source, Target& target) { return invoke_functor(priority_tag_0{}, Functor{}, event, fsm, source, target); } }; template <> struct invoke_guard_functor { template static bool execute(const Event&, Fsm&, Source&, Target&) { return true; } }; template using get_Action = typename Row::Action; template struct has_Action : mp11::mp_valid {}; template struct invoke_action_functor { template static process_result execute(const Event& event, Fsm& fsm, Source& source, Target& target) { invoke_functor(priority_tag_0{}, Functor{}, event, fsm, source, target); return process_result::HANDLED_TRUE; } }; template <> struct invoke_action_functor { template static process_result execute(const Event&, Fsm&, Source&, Target&) { return process_result::HANDLED_TRUE; } }; template <> struct invoke_action_functor { template static process_result execute(const Event& event, Fsm& fsm, Source&, Target&) { fsm.defer_event(event); return process_result::HANDLED_DEFERRED; } }; template struct transition_table_impl { using front_end_t = typename StateMachine::front_end_t; using derived_t = typename StateMachine::derived_t; using state_set = typename StateMachine::state_set; template using get_active_state_switch_policy = typename T::active_state_switch_policy; using active_state_switching = boost::mp11::mp_eval_or; template static bool call_guard_or_true(StateMachine& sm, const Event& event, Source& source, Target& target) { if constexpr (has_Guard::value) { return invoke_guard_functor::execute( event, sm.get_fsm_argument(), source, target); } else if constexpr (HasGuard) { return Row::guard_call( sm.get_fsm_argument(), event, source, target, sm.m_states); } else { return true; } } template static process_result call_action_or_true(StateMachine& sm, const Event& event, Source& source, Target& target) { if constexpr (has_Action::value) { return invoke_action_functor::execute( event, sm.get_fsm_argument(), source, target); } else if constexpr (HasAction) { return Row::action_call( sm.get_fsm_argument(), event, source, target, sm.m_states); } else { return process_result::HANDLED_TRUE; } } template using get_state = typename DirectWrapper::state; template using is_explicit_entry_point = mp11::mp_eval_if< mpl::is_sequence, mp11::mp_true, has_explicit_entry_be_tag, FeTarget>; template static void call_entry(StateMachine& sm, const Event& event, Target& target) { using FeTarget = typename Row::Target; auto& fsm = sm.get_fsm_argument(); if constexpr (is_explicit_entry_point::value) { using targets = to_mp_list_t; using states = mp11::mp_transform; target.template on_explicit_entry(event, fsm); } else if constexpr (has_entry_pseudostate_be_tag::value) { using targets = to_mp_list_t; using states = mp11::mp_transform; target.template on_pseudo_entry(event, fsm); } else { target.on_entry(event, fsm); if constexpr (has_exit_pseudostate_be_tag::value) { // Execute the second part of the compound transition. target.forward_event(*sm.m_root_sm, event); } } } // Template used to create transitions from rows in the transition table. template struct transition { 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(StateMachine& sm, int region_id, transition_event const& event) { int& state_id = sm.m_active_state_ids[region_id]; static constexpr int current_state_id = StateMachine::template get_state_id(); static constexpr int next_state_id = StateMachine::template get_state_id(); BOOST_ASSERT(state_id == current_state_id); auto& source = sm.template get_state(); auto& target = sm.template get_state(); if (!call_guard_or_true(sm, event, source, target)) { // guard rejected the event, we stay in the current one return process_result::HANDLED_GUARD_REJECT; } 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()); 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); state_id = active_state_switching::after_action(current_state_id, next_state_id); // and finally the entry method of the new state call_entry(sm, event, target); state_id = active_state_switching::after_entry(current_state_id, next_state_id); // Give a chance to handle completion transitions. sm.template on_state_entry_completed(region_id); return res; } }; // Template used to create internal transitions // from rows in the transition table. template struct internal_transition { using transition_event = typename Row::Evt; using current_state_type = State; using next_state_type = current_state_type; // Take the transition action and return the next state. static process_result execute(StateMachine& sm, int region_id, transition_event const& event) { [[maybe_unused]] const int state_id = sm.m_active_state_ids[region_id]; BOOST_ASSERT( state_id == StateMachine::template get_state_id()); auto& source = sm.template get_state(); auto& target = source; if (!call_guard_or_true(sm, event, source, target)) { return process_result::HANDLED_GUARD_REJECT; } return call_action_or_true(sm, event, source, target); } }; // Template used to create sm-internal transitions // from rows in the transition table. template struct internal_transition { using transition_event = typename Row::Evt; // Take the transition action and return the next state. static process_result execute(StateMachine& sm, transition_event const& event) { auto& source = sm; auto& target = source; if (!call_guard_or_true(sm, event, source, target)) { return process_result::HANDLED_GUARD_REJECT; } return call_action_or_true(sm, event, source, target); } }; // Helpers used to create back-end transitions // from front-end transitions. template struct create_transition_impl; template struct create_transition_impl { using type = transition; }; template struct create_transition_impl { using type = transition; }; template struct create_transition_impl<_row_tag, Row, State> { using type = transition; }; template struct create_transition_impl { using type = transition; }; template struct create_transition_impl { using type = internal_transition; }; template struct create_transition_impl { using type = internal_transition; }; template struct create_transition_impl { using type = internal_transition; }; template struct create_transition_impl<_irow_tag, Row, State> { using type = internal_transition; }; template struct create_transition_impl { using type = internal_transition; }; template struct create_transition_impl { using type = internal_transition; }; template struct create_transition_impl { using type = internal_transition; }; template struct create_transition_impl { using type = internal_transition; }; template using create_transition = typename create_transition_impl::type; // Transform the front-end's internal transition table // to a back-end transition table, // with reversed ordering for backward compatibility. using internal_transition_table = mp11::mp_reverse< mp11::mp_transform_q< mp11::mp_bind_back, to_mp_list_t>>; // Expand the front-end's transition table(s) // to a single back-end transition table, // with reversed ordering for backward compatibility. // First transform the main transition table of the SM. using sm_transition_table = mp11::mp_transform_q< mp11::mp_bind_back, to_mp_list_t>; // Then transform and append the states' internal transition tables. template using append_state_transition_tables_predicate = mp11::mp_or< is_composite, mp11::mp_empty>>; template using append_internal_transition_table = mp11::mp_append< TransitionTable, mp11::mp_transform_q< mp11::mp_bind_back, to_mp_list_t>>; using transition_table = mp11::mp_reverse< mp11::mp_fold< mp11::mp_remove_if, sm_transition_table, append_internal_transition_table>>; // Completion transitions are handled separately per state. template using has_completion_event = has_completion_event; using completion_transition_table = mp11::mp_copy_if; static_assert( mp11::mp_empty::value || !std::is_same_v< typename StateMachine::template event_container, no_event_container>, "Completion transitions require an event pool"); template struct completion_transitions_impl { template using predicate = std::is_same; using type = mp11::mp_copy_if; }; template struct completion_transitions_impl> { using type = mp11::mp_list<>; }; template using completion_transitions = typename completion_transitions_impl::type; }; template using transition_table = typename transition_table_impl::transition_table; template using internal_transition_table = typename transition_table_impl::internal_transition_table; template using completion_transitions = typename transition_table_impl::template completion_transitions; template using has_completion_transitions = mp11::mp_not>>; // Class used to execute a chain of transitions for a given event and state. // Handles transition conflicts. template struct transition_chain { using current_state_type = State; using transition_event = Event; static process_result execute(StateMachine& sm, int region_id, Event const& evt) { process_result result = process_result::HANDLED_FALSE; mp_for_each_until( [&result, &sm, region_id, &evt](auto transition) { using Transition = decltype(transition); result |= Transition::execute(sm, region_id, evt); if (result & handled_true_or_deferred) { // If a guard rejected previously, ensure this bit is not present. result &= handled_true_or_deferred; return true; } return false; } ); return result; } }; } // namespace boost::msm::backmp11::detail #endif // BOOST_MSM_BACKMP11_DETAIL_TRANSITION_TABLE_HPP