// 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_FAVOR_COMPILE_TIME_HPP #define BOOST_MSM_BACKMP11_FAVOR_COMPILE_TIME_HPP #include #include #include #include #include #include #include #include // Macro to generate the dispatch_table for a specific state machine type. #define BOOST_MSM_BACKMP11_GENERATE_STATE_MACHINE(SM_TYPE) \ namespace boost::msm::backmp11::detail \ { \ template <> \ const typename compile_policy_impl< \ favor_compile_time>::template dispatch_table& \ compile_policy_impl::dispatch_table< \ SM_TYPE, any_event>::instance() \ { \ static dispatch_table table; \ return table; \ } \ } // boost::msm::backmp11::detail namespace boost:: msm::backmp11 { struct favor_compile_time { // TODO fix adapter and remove this. using compile_policy = int; }; namespace detail { using any_event = std::any; template <> struct compile_policy_impl { // Type-punned init cell value to suppress redundant template instantiations. using generic_cell = void(*)(); // GCC cannot recognize the parameter pack in the array's initializer. #if !defined(__GNUC__) || defined(__clang__) // Convert a list with integral constants of the same value_type to a value array // (non-constexpr version required due to typeid). template struct value_array; template struct value_array> { using value_type = typename mp11::mp_front>::value_type; const value_type value[sizeof...(Ts)]{Ts{}.value...}; }; #endif template static any_event normalize_event(const Event& event) { return any_event{event}; } constexpr static const any_event& normalize_event(const any_event& event) { return event; } template static bool is_end_interrupt_event(const StateMachine& sm, const any_event& event) { using event_set = generate_event_set< typename StateMachine::front_end_t::transition_table>; bool result{false}; mp11::mp_for_each>( [&sm, &event, &result](auto event_identity) { using Event = typename decltype(event_identity)::type; using Flag = EndInterruptFlag; if (event.type() == typeid(Event)) { result = sm.template is_flag_active(); } }); return result; } // Dispatch table for event deferral checks. // Converts any_events and calls 'is_event_deferred' on states. class is_event_deferred_dispatch_table { public: template static bool dispatch(const State& state, const any_event& event, const Fsm& fsm) { static const is_event_deferred_dispatch_table table{state, fsm}; auto it = table.m_cells.find(event.type()); if (it != table.m_cells.end()) { using real_cell = bool (*)(const State&, const any_event&, const Fsm&); auto cell = reinterpret_cast(it->second); return (*cell)(state, event, fsm); } return false; } private: template is_event_deferred_dispatch_table(const State&, const Fsm&) { using deferred_events = to_mp_list_t; using deferred_event_identities = mp11::mp_transform; mp11::mp_for_each( [this](auto event_identity) { using Event = typename decltype(event_identity)::type; m_cells[to_type_index()] = reinterpret_cast(&convert_and_execute); }); } template static bool convert_and_execute(const State& state, const any_event& event, const Fsm& fsm) { return state.is_event_deferred(*any_cast(&event), fsm); } std::unordered_map m_cells; }; class is_event_deferred_visitor : public is_event_deferred_visitor_base { public: is_event_deferred_visitor(const any_event& event) : m_event(event) { } template void operator()(const State& state, const Fsm& fsm) { using table = is_event_deferred_dispatch_table; m_result |= table::dispatch(state, m_event, fsm); } private: const any_event& m_event; }; template static bool is_event_deferred(const StateMachine& sm, const any_event& event) { using visitor_t = is_event_deferred_visitor; using state_visitor = event_deferral_visitor; if constexpr (state_visitor::needs_traversal::value) { visitor_t visitor{event}; state_visitor::visit(sm, visitor); return visitor.result(); } return false; } template static void defer_event(StateMachine& sm, any_event const& event, bool next_rtc_seq) { sm.do_defer_event(event, next_rtc_seq); } // Convert an event to a type index. template static std::type_index to_type_index() { return std::type_index{typeid(Event)}; } // Class used to build a chain of transitions for a given event and state. // Allows transition conflicts. class transition_chain { public: template process_result execute(StateMachine& sm, int region_id, any_event const& event, process_result result) const { using cell_t = process_result (*)(StateMachine&, int, any_event const&); for (const generic_cell cell : m_transition_cells) { result |= reinterpret_cast(cell)(sm, region_id, event); if (result & handled_true_or_deferred) { // If a guard rejected previously, ensure this bit is not present. return result & handled_true_or_deferred; } } // At this point result can be HANDLED_FALSE or HANDLED_GUARD_REJECT. return result; } template void add_transition_cell(process_result (*cell)(StateMachine&, int /*region_id*/, any_event const&)) { m_transition_cells.emplace_back( reinterpret_cast(cell)); } private: std::vector m_transition_cells; }; // Class used to build a chain of sm-internal transitions for a given event and state. // Allows transition conflicts. class internal_transition_chain { public: template process_result execute(StateMachine& sm, any_event const& event) const { using cell_t = process_result (*)(StateMachine&, any_event const&); process_result result = process_result::HANDLED_FALSE; for (const generic_cell cell : m_transition_cells) { result |= reinterpret_cast(cell)(sm, event); if (result & handled_true_or_deferred) { // If a guard rejected previously, ensure this bit is not present. return result & handled_true_or_deferred; } } // At this point result can be HANDLED_FALSE or HANDLED_GUARD_REJECT. return result; } template void add_transition_cell(process_result (*cell)(StateMachine&, any_event const&)) { m_transition_cells.emplace_back( reinterpret_cast(cell)); } private: std::vector m_transition_cells; }; // 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 class dispatch_table; template class dispatch_table { public: // Dispatch an event. static process_result dispatch(StateMachine& sm, int region_id, const any_event& event) { const int state_id = sm.m_active_state_ids[region_id]; const dispatch_table& self = instance(); return self.m_state_dispatch_tables[state_id].dispatch(sm, region_id, event); } // Dispatch an event to the SM's internal table. static process_result internal_dispatch(StateMachine& sm, const any_event& event) { if constexpr (has_internal_transitions::value) { const dispatch_table& self = instance(); return self.m_internal_dispatch_table.dispatch(sm, event); } return process_result::HANDLED_FALSE; } private: using state_set = typename StateMachine::internal::state_set; using submachines = mp11::mp_copy_if; // Value used to initialize a cell of the dispatch table. using cell_t = process_result (*)(StateMachine&, int /*region_id*/, any_event const&); struct init_cell_value { std::type_index event_type_index; size_t state_id; cell_t cell; }; template struct init_cell_constant { using value_type = init_cell_value; const value_type value = {typeid(Event), index, cell}; }; template static process_result convert_event_and_execute( StateMachine& sm, int region_id, const any_event& event) { return Transition::execute(sm, region_id, *any_cast(&event)); } template struct get_init_cell_constant_impl { using type = init_cell_constant< typename Transition::transition_event, StateMachine::template get_state_id(), convert_event_and_execute >; }; template using get_init_cell_constant = typename get_init_cell_constant_impl::type; using has_internal_transitions = mp11::mp_not>>; using has_transitions = mp11::mp_not>>; // Dispatch table for one state. class state_dispatch_table { public: // Initialize the call to the composite state's process_event function. template void init_composite_state() { m_call_process_event = &call_process_event; } // Add a cell to the dispatch table. void add_transition_cell(const init_cell_value& value) { transition_chain& chain = m_transition_chains[value.event_type_index]; chain.add_transition_cell(value.cell); } // Dispatch an event. process_result dispatch(StateMachine& sm, int region_id, const any_event& event) const { process_result result = process_result::HANDLED_FALSE; if (m_call_process_event) { result = m_call_process_event(sm, event); if (result & handled_true_or_deferred) { return result; } } auto it = m_transition_chains.find(event.type()); if (it != m_transition_chains.end()) { result = (it->second.execute)(sm, region_id, event, result); } return result; } private: template static process_result call_process_event(StateMachine& sm, const any_event& event) { return sm.template get_state() .process_event_internal(event, process_info::submachine_call); } 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}; }; // Value used to initialize a cell of the sm-internal dispatch table. using internal_cell_t = process_result (*)(StateMachine&, any_event const&); struct init_internal_cell_value { std::type_index event_type_index; internal_cell_t cell; }; template struct init_internal_cell_constant { using value_type = init_internal_cell_value; const value_type value = {typeid(Event), cell}; }; template static process_result convert_event_and_execute_internal( StateMachine& sm, const any_event& event) { return Transition::execute(sm, *any_cast(&event)); } template struct get_internal_init_cell_constant_impl { using type = init_internal_cell_constant< typename Transition::transition_event, convert_event_and_execute_internal >; }; template using get_internal_init_cell_constant = typename get_internal_init_cell_constant_impl::type; // Dispatch table for sm-internal transitions. class internal_dispatch_table { public: // Add a cell to the dispatch table. void add_transition_cell(const init_internal_cell_value& value) { internal_transition_chain& chain = m_transition_chains[value.event_type_index]; chain.add_transition_cell(value.cell); } // Dispatch an event. process_result dispatch(StateMachine& sm, const any_event& event) const { process_result result = process_result::HANDLED_FALSE; auto it = m_transition_chains.find(event.type()); if (it != m_transition_chains.end()) { result = (it->second.execute)(sm, event); } return result; } private: std::unordered_map m_transition_chains; }; dispatch_table() { // Initialize the state dispatch tables. using submachines = mp11::mp_copy_if; mp11::mp_for_each>( [this](auto state_identity) { using Submachine = typename decltype(state_identity)::type; static constexpr int state_id = StateMachine::template get_state_id(); m_state_dispatch_tables[state_id].template init_composite_state(); }); if constexpr (has_transitions::value) { using init_cell_constants = mp11::mp_transform< get_init_cell_constant, transition_table>; #if defined(__GNUC__) && !defined(__clang__) mp11::mp_for_each( [this](auto constant) { m_state_dispatch_tables[constant.value.state_id].add_transition_cell(constant.value); }); #else value_array value_array; for (const init_cell_value& value: value_array.value) { m_state_dispatch_tables[value.state_id].add_transition_cell(value); } #endif } // Initialize the sm-internal dispatch table. if constexpr (has_internal_transitions::value) { using init_internal_cell_constants = mp11::mp_transform< get_internal_init_cell_constant, internal_transition_table>; #if defined(__GNUC__) && !defined(__clang__) mp11::mp_for_each( [this](auto constant) { m_internal_dispatch_table.add_transition_cell(constant.value); }); #else value_array value_array; for (const init_internal_cell_value& value: value_array.value) { m_internal_dispatch_table.add_transition_cell(value); } #endif } } // The singleton instance. static const dispatch_table& instance(); // We need one dispatch table per state. state_dispatch_table m_state_dispatch_tables[mp11::mp_size::value]; internal_dispatch_table m_internal_dispatch_table; }; }; #ifndef BOOST_MSM_BACKMP11_MANUAL_GENERATION template const typename compile_policy_impl::template dispatch_table& compile_policy_impl::dispatch_table::instance() { static dispatch_table table; return table; } #endif } // detail } // boost::msm::backmp11 #endif //BOOST_MSM_BACKMP11_FAVOR_COMPILE_TIME_HPP