#pragma once #include namespace hsm { /* * Chain abitrary number of actions to be used in the single transition * * @param[in] actions actions that will be chained */ constexpr auto chain_actions = [](auto... actions) { return [=](auto&&... args) { return std::apply([&](auto... f) { (f(args...), ...); }, std::tie(actions...)); }; }; /* * Shortcut for chain_actions */ constexpr auto chain = chain_actions; } namespace hsm { template class CreateState { public: constexpr CreateState(StateFactory stateFactory) : m_stateFactory(stateFactory) { } template constexpr auto operator()(Event event, Source source, Target target) const { *target = m_stateFactory(event, source); } private: StateFactory m_stateFactory; }; template constexpr auto create_state(StateFactory stateFactory) { return CreateState { stateFactory }; } } #include #include namespace hsm { constexpr auto log = [](const auto& event, const auto& source, const auto& target, const auto&...) { std::cout << boost::hana::experimental::print(boost::hana::typeid_(source)) << " + " << boost::hana::experimental::print(boost::hana::typeid_(event)) << " = " << boost::hana::experimental::print(boost::hana::typeid_(target)) << std::endl; }; } namespace hsm { template class ReuseState { public: constexpr ReuseState(Action action) : m_action(action) { } template constexpr auto operator()(Event event, Source source, Target target) const { m_action(event, source, **target); } private: Action m_action; }; template constexpr auto reuse_state(Action action) { return ReuseState { action }; } } #include namespace hsm { namespace bh { using namespace boost::hana; } inline constexpr auto for_each_idx = [](auto list, auto closure) { std::size_t index = 0; bh::for_each(list, [closure, &index](const auto& elem) { closure(elem, index); index++; }); }; } #include #include #include #include #include namespace hsm { namespace bh { using namespace boost::hana; } namespace { constexpr auto to_type_pair = [](auto x) { return bh::make_pair(bh::typeid_(x), x); }; } template constexpr auto remove_duplicates(Tuple tuple) { return boost::mp11::mp_unique> {}; } template constexpr auto remove_duplicate_types(Tuple tuple) { return bh::values(bh::to_map(bh::transform(tuple, to_type_pair))); } } namespace hsm { struct noAction { }; struct noGuard { }; template struct state_t; template class PseudoState { public: constexpr PseudoState(ParentState parentState, State state) : parentState(parentState) , state(state) { } constexpr auto get_parent_state() { return parentState; } constexpr auto get_state() { return state; } private: ParentState parentState; State state; }; class ExitPseudoState { }; class EntryPseudoState { }; class DirectPseudoState { }; class HistoryPseudoState { }; class InitialPseudoState { }; template class Exit final : public PseudoState, public ExitPseudoState { public: constexpr Exit() : PseudoState(ParentState {}, State {}) { } }; template class Entry final : public PseudoState, public EntryPseudoState { public: constexpr Entry() : PseudoState(ParentState {}, State {}) { } }; template class Direct final : public PseudoState, public DirectPseudoState { public: constexpr Direct() : PseudoState(ParentState {}, State {}) { } }; template class History final : public HistoryPseudoState { public: constexpr History() : parentState(ParentState {}) { } constexpr auto get_parent_state() { return parentState; } private: ParentState parentState; }; template class Initial final : public InitialPseudoState { public: constexpr Initial() : state(State {}) { } constexpr auto get_state() { return state; } private: State state; }; } #include #include #include #include #include #include #include #include #include #include #include #include namespace hsm { namespace bh { using namespace boost::hana; } namespace details { constexpr auto has_internal_transition_table = bh::is_valid([](auto&& state) -> decltype(state.make_internal_transition_table()) {}); } constexpr auto is_default_constructable = bh::is_valid([](auto typeid_) -> decltype(typename decltype(typeid_)::type()) {}); constexpr auto is_custom_target_action = bh::is_valid([](auto action, auto... args) -> decltype(action(args...)) {}); auto const is_callable = [](auto&& callable, auto&& args) { return bh::unpack(args, bh::is_valid([](auto&&... args) -> decltype(callable(args...)) {})); }; constexpr auto get_parent_state = [](auto state) { return decltype(std::declval().get_parent_state())(); }; constexpr auto get_state = [](auto state) { return decltype(std::declval().get_state())(); }; constexpr auto unwrap_typeid = [](auto typeid_) { return typename decltype(typeid_)::type {}; }; constexpr auto unwrap_typeid_to_shared_ptr = [](auto typeid_) { using UnwrappedType = typename decltype(typeid_)::type; if constexpr (is_default_constructable(typeid_)) { return std::make_shared>(std::make_unique()); } else { return std::make_shared>(nullptr); } }; constexpr auto make_transition_table = [](auto t) { return decltype(std::declval().make_transition_table())(); }; constexpr auto make_internal_transition_table = [](auto state) { return decltype(state)::type::make_internal_transition_table(); }; constexpr auto make_transition_table2 = [](auto state) { return decltype(state)::type::make_transition_table(); }; constexpr auto has_transition_table = bh::is_valid( [](auto stateTypeid) -> decltype(std::declval() .make_transition_table()) {}); constexpr auto has_internal_transition_table = bh::is_valid( [](auto stateTypeid) -> decltype(std::declval() .make_internal_transition_table()) {}); constexpr auto has_entry_action = bh::is_valid( [](auto stateTypeid) -> decltype( std::declval().on_entry()) {}); constexpr auto has_exit_action = bh::is_valid( [](auto stateTypeid) -> decltype( std::declval().on_exit()) {}); constexpr auto has_unexpected_event_handler = bh::is_valid( [](auto stateTypeid) -> decltype(std::declval().on_unexpected_event()) {}); constexpr auto has_deferred_events = bh::is_valid( [](auto stateTypeid) -> decltype(std::declval().defer_events()) {}); constexpr auto is_exit_state = [](auto stateTypeid) { return bh::equal( bh::bool_c::value>, bh::true_c); }; constexpr auto is_entry_state = [](auto stateTypeid) { return bh::equal( bh::bool_c::value>, bh::true_c); }; constexpr auto is_direct_state = [](auto stateTypeid) { return bh::equal( bh::bool_c::value>, bh::true_c); }; constexpr auto is_history_state = [](auto stateTypeid) { return bh::equal( bh::bool_c< std::is_base_of::value>, bh::true_c); }; constexpr auto is_initial_state = [](auto typeid_) { return bh::equal( bh::bool_c::value>, bh::true_c); }; constexpr auto is_no_action = [](auto action) { return bh::equal(bh::typeid_(action), bh::typeid_(noAction {})); }; template constexpr decltype(auto) is_action() { return bh::not_equal(bh::type_c, bh::typeid_(noAction {})); } constexpr auto is_no_guard = [](auto guard) { return bh::equal(bh::typeid_(guard), bh::typeid_(noGuard {})); }; template constexpr decltype(auto) is_guard() { return bh::not_equal(bh::type_c, bh::typeid_(noGuard {})); } constexpr auto is_event = bh::is_valid([](auto&& event) -> decltype(event.typeid_) {}); constexpr auto get_entry_action = [](auto stateTypeid) { return decltype(stateTypeid)::type::on_entry(); }; constexpr auto get_exit_action = [](auto stateTypeid) { return decltype(stateTypeid)::type::on_exit(); }; constexpr auto get_defer_events = [](auto stateTypeid) { return decltype(stateTypeid)::type::defer_events(); }; const auto get_unexpected_event_handler = [](auto rootState) { if constexpr (has_unexpected_event_handler(rootState)) { return decltype(rootState)::type::on_unexpected_event(); } else { return [](auto&&...) {}; } }; } #include #include #include namespace hsm { namespace bh { using namespace boost::hana; } namespace { const auto collectGuard = [](auto transition) { return bh::at_c<3>(transition); }; } const auto collect_guards_recursive = [](auto state) { auto collectedGuards = bh::transform(flatten_transition_table(state), collectGuard); return remove_duplicate_types(collectedGuards); }; const auto collect_guard_typeids_recursive = [](auto state) { return bh::transform(collect_guards_recursive(state), bh::typeid_); }; } #include #include #include namespace hsm { namespace bh { using namespace boost::hana; } template constexpr auto collect_parent_state_typeids(State state) { auto toParentStateTypeid = [](auto transition) { return bh::typeid_(transition.parent()); }; auto transitions = flatten_transition_table(state); auto parentStateTypeids = bh::transform(transitions, toParentStateTypeid); return remove_duplicates(parentStateTypeids); } } namespace hsm { template struct event_t; struct noneEvent; struct noAction; struct noGuard; template class TransitionEG; template class TransitionEA; template class TransitionEGA; template class TransitionSA; template class TransitionSG; template class TransitionSE; template class TransitionSEG; template class TransitionSEA; template class TransitionSEGA; template struct initial_t; } #include #include namespace hsm { namespace details { namespace bh { using namespace boost::hana; } template struct Transition { constexpr Transition(Source, Event, Guard guard, Action action, Target) : m_guard(guard) , m_action(action) { } [[nodiscard]] constexpr auto source() const -> Source { return Source {}; } [[nodiscard]] constexpr auto event() const -> Event { return Event {}; } [[nodiscard]] constexpr auto action() const -> Action { return m_action; } [[nodiscard]] constexpr auto guard() const -> Guard { return m_guard; } [[nodiscard]] constexpr auto target() const -> Target { return Target {}; } private: const Guard m_guard; const Action m_action; }; template struct InternalTransition { constexpr InternalTransition(Event, Guard guard, Action action) : m_guard(guard) , m_action(action) { } constexpr auto event() const -> Event { return Event {}; } constexpr auto action() const -> Action { return m_action; } constexpr auto guard() const -> Guard { return m_guard; } private: const Guard m_guard; const Action m_action; }; template < class Parent, class Source, class Event, class Guard, class Action, class Target, bool Internal> struct ExtendedTransition { constexpr ExtendedTransition( Parent, Source, Event, Guard guard, Action action, Target) : m_guard(guard) , m_action(action) { } [[nodiscard]] constexpr auto parent() const -> Parent { return Parent {}; } [[nodiscard]] constexpr auto source() const -> Source { return Source {}; } [[nodiscard]] constexpr auto event() const -> Event { return Event {}; } [[nodiscard]] constexpr auto action() const -> Action { return m_action; } [[nodiscard]] constexpr auto guard() const -> Guard { return m_guard; } [[nodiscard]] constexpr auto target() const -> Target { return Target {}; } [[nodiscard]] constexpr auto internal() const -> bool { return Internal; } private: const Guard m_guard; const Action m_action; }; constexpr auto transition = [](auto&&... xs) { return Transition...> { xs... }; }; constexpr auto internal_transition = [](auto&&... xs) { return InternalTransition...> { xs... }; }; constexpr auto extended_transition = [](auto parent, auto transition) { return ExtendedTransition< std::decay_t, std::decay_t, std::decay_t, std::decay_t, std::decay_t, std::decay_t, false> { parent, transition.source(), transition.event(), transition.guard(), transition.action(), transition.target() }; }; constexpr auto internal_extended_transition = [](auto parent, auto transition) { return ExtendedTransition< std::decay_t, std::decay_t, std::decay_t, std::decay_t, std::decay_t, std::decay_t, true> { parent, transition.source(), transition.event(), transition.guard(), transition.action(), transition.target() }; }; } } #include #include namespace hsm { namespace bh { using namespace boost::hana; } template struct event_t { static constexpr bh::type typeid_ {}; constexpr auto operator+() const { return details::internal_transition(event_t {}, noGuard {}, noAction {}); } template constexpr auto operator[](const Guard& guard) const { return TransitionEG, Guard> { guard }; } template constexpr auto operator/(const Action& guard) const { return TransitionEA, Action> { guard }; } }; template const event_t event {}; struct noneEvent { }; using none = event_t; } #include #include #include namespace hsm { namespace bh { using namespace boost::hana; } template constexpr auto to_pairs(const Tuple& tuple) { return bh::transform( tuple, [](auto tuple) { return bh::make_pair(bh::at_c<0>(tuple), bh::at_c<1>(tuple)); }); } } #include #include #include #include #include #include #include #include namespace { template class GetIdx; template class GetIdx> { template static constexpr auto find_idx(std::index_sequence seq) -> std::size_t { return seq.size() + ((std::is_same::value ? idx - seq.size() : 0) + ...); } public: static constexpr std::size_t value = find_idx(std::index_sequence_for {}); }; template class GetIdx> { template static constexpr auto find_idx(std::index_sequence seq) -> std::size_t { return seq.size() + ((std::is_same::value ? idx - seq.size() : 0) + ...); } public: static constexpr std::size_t value = find_idx(std::index_sequence_for {}); }; } namespace hsm { namespace bh { using namespace boost::hana; } template constexpr auto index_of(Iterable const&, Element const&) { return GetIdx::value; } template constexpr auto make_index_map(Typeids typeids) { return bh::apply( [](auto typeids, auto range) { return bh::to_map(to_pairs(bh::zip(typeids, range))); }, typeids, bh::to(bh::make_range(bh::int_c<0>, bh::size(typeids)))); } // constexpr auto find = [](auto&& reverseIndexMap, auto index, auto&& closure) { // boost::mp11::mp_with_index( // index, // [reverseIndexMap, &closure](auto i) { closure(bh::find(reverseIndexMap, i).value()); }); // }; // constexpr auto make_reverse_index_map = [](auto&& tuple) { // auto range = bh::to(bh::make_range(bh::int_c<0>, bh::size(tuple))); // auto ids // = bh::transform(range, [](auto&& element) { return boost::mp11::mp_size_t {}; }); // return bh::to_map(to_pairs(bh::zip(ids, tuple))); // }; } #include #include #include #include #include #include #include namespace hsm { namespace bh { using namespace boost::hana; } using Idx = std::uint64_t; using StateIdx = Idx; using EventIdx = Idx; using ActionIdx = Idx; using GuardIdx = Idx; constexpr auto getIdx = [](auto map, auto type) -> Idx { return bh::find(map, type).value(); }; constexpr auto getParentStateIdx = [](auto rootState, auto parentState) { return index_of(collect_parent_state_typeids(rootState), bh::typeid_(parentState)); }; constexpr auto getStateIdx = [](auto rootState, auto state) { return index_of(collect_state_typeids_recursive(rootState), bh::typeid_(state)); }; template constexpr auto getCombinedStateTypeids(const State& rootState) { auto parentStateTypeids = collect_parent_state_typeids(rootState); auto stateTypeids = collect_state_typeids_recursive(rootState); auto stateCartesianProduct = bh::cartesian_product(bh::make_basic_tuple(parentStateTypeids, stateTypeids)); return bh::transform(stateCartesianProduct, bh::typeid_); } template constexpr auto getCombinedStateTypeid(const ParentState& parentState, const State& state) { return bh::typeid_(bh::make_basic_tuple(bh::typeid_(parentState), bh::typeid_(state))); } template constexpr auto getCombinedStateIdx(StateTypeids combinedStateTypids, ParentState parentState, State state) { return index_of(combinedStateTypids, getCombinedStateTypeid(parentState, state)); } constexpr auto calcCombinedStateIdx = [](std::size_t nStates, Idx parentStateIdx, Idx stateIdx) -> Idx { return (parentStateIdx * nStates) + stateIdx; }; constexpr auto calcParentStateIdx = [](std::size_t nStates, Idx combinedState) -> Idx { return combinedState / nStates; }; constexpr auto calcStateIdx = [](std::size_t nStates, Idx combinedState) -> Idx { return combinedState % nStates; }; template constexpr decltype(auto) resolveEvent(Event event) { if constexpr (is_event(event)) { return event.typeid_; } else { return bh::typeid_(event); } } constexpr auto getEventIdx = [](auto rootState, auto event) { return index_of(collect_event_typeids_recursive(rootState), resolveEvent(event)); }; constexpr auto getActionIdx = [](auto rootState, auto action) { return index_of(collect_action_typeids_recursive(rootState), bh::typeid_(action)); }; constexpr auto getGuardIdx = [](auto rootState, auto guard) { return index_of(collect_guard_typeids_recursive(rootState), bh::typeid_(guard)); }; } #include #include #include #include #include #include #include namespace hsm { /** * Collect the initial states for the parent states * * Returns: [[State]] * */ template constexpr auto collect_initial_states(State parentState) { constexpr auto childStates = collect_child_states(parentState); constexpr auto initialStates = bh::filter(childStates, is_initial_state); return bh::transform(initialStates, [](auto initialState) { return get_state(initialState); }); } /** * Collect the initial states for the parent states * and returns it as tuple of combined state idx. * * Returns: [[StateIdx]] * * Example: [[0,1], [0], [1], [1,2]] */ template constexpr auto collect_initial_state_stateidx(State rootState, Typeids parentStateTypeids) { return bh::transform(parentStateTypeids, [rootState](auto parentStateTypeid) { using ParentState = typename decltype(parentStateTypeid)::type; constexpr auto initialStates = collect_initial_states(ParentState {}); return bh::transform(initialStates, [rootState](auto initialState) { return getCombinedStateIdx( getCombinedStateTypeids(rootState), ParentState {}, initialState); }); }); } /** * Return a map from parent state id to inital state ids * * Returns: (ParentStateIdx -> [StateIdx]) * * Example: * [[0 -> [0, 1]], * [1 -> [3, 1]], * [2 -> [0, 2]]] */ template inline constexpr auto make_initial_state_map(State rootState) { constexpr auto parentStateTypeids = collect_parent_state_typeids(rootState); constexpr auto initialStates = collect_initial_state_stateidx(rootState, parentStateTypeids); return bh::to_map(to_pairs(bh::zip(parentStateTypeids, initialStates))); } /** * Fills the initial state table with the state idx of the initial * states. * * Parameters: * initialStateTable : [[StateIdx]] * initialStateTable[parentStateIdx].size() is the number regions * in the parent state. * initialStateTable[parentStateIdx][regionIdx] is a state idx. */ template constexpr auto fill_initial_state_table(State rootState, StateTable& initialStateTable) { constexpr auto parentStateTypeids = collect_parent_state_typeids(rootState); for_each_idx( parentStateTypeids, [rootState, &initialStateTable](auto parentStateTypeid, auto parentStateId) { bh::apply( [](auto parentStateId, auto& initialStateTable, auto initialStates) { auto initialStatesStateIdx = std::vector(bh::size(initialStates)); for_each_idx( initialStates, [&initialStatesStateIdx](auto stateIdx, auto regionId) { initialStatesStateIdx[regionId] = stateIdx; }); initialStateTable.at(parentStateId) = initialStatesStateIdx; }, parentStateId, initialStateTable, bh::find(make_initial_state_map(rootState), parentStateTypeid).value()); }); } /** * Returns a tuple of initial state sizes * * Returns: [std::size_t] * * Example: [3, 1, 2] */ template constexpr auto initialStateSizes(Typeids parentStateTypeids) { return bh::transform(parentStateTypeids, [](auto parentStateTypeid) { using ParentState = typename decltype(parentStateTypeid)::type; return bh::size(collect_initial_states(ParentState {})); }); } /** * Returns the maximal number of initial states */ template constexpr auto maxInitialStates(State rootState) { return bh::maximum(initialStateSizes(collect_parent_state_typeids(rootState))); } } #include #include #include #include namespace hsm { namespace bh { using namespace boost::hana; } constexpr auto has_substate_initial_state_entry_action = [](auto target) { if constexpr (has_transition_table(target)) { return has_entry_action(bh::at_c<0>(collect_initial_states(target))); } else { return bh::false_c; } }; constexpr auto has_pseudo_exit_action = [](auto transition) { return bh::and_( is_exit_state(transition.source()), has_exit_action(resolveSrcParent(transition))); }; constexpr auto has_action = [](auto transition) { return bh::or_( bh::not_(is_no_action(transition.action())), has_entry_action(transition.target()), has_substate_initial_state_entry_action(transition.target()), has_exit_action(transition.source()), has_pseudo_exit_action(transition)); }; } #include namespace hsm { namespace bh { using namespace boost::hana; } template constexpr auto resolveSubStateParent(Transition transition) { auto constexpr target = transition.target(); if constexpr (is_entry_state(target)) { return get_parent_state(target); } else if constexpr (is_direct_state(target)) { return get_parent_state(target); } else if constexpr (is_history_state(target)) { return get_parent_state(target); } else { return target; } } template constexpr auto resolveDst(Transition transition) { auto constexpr dst = transition.target(); if constexpr (has_transition_table(dst)) { return bh::at_c<0>(collect_initial_states(dst)); } else if constexpr (is_entry_state(dst)) { return get_state(dst); } else if constexpr (is_direct_state(dst)) { return get_state(dst); } else if constexpr (is_history_state(dst)) { return bh::at_c<0>(collect_initial_states(get_parent_state(dst))); } else if constexpr (is_initial_state(dst)) { return get_state(dst); } else { return dst; } } template constexpr auto resolveDstParent(Transition transition) { auto constexpr target = transition.target(); if constexpr (has_transition_table(target)) { return target; } else if constexpr (is_entry_state(target)) { return get_parent_state(target); } else if constexpr (is_direct_state(target)) { return get_parent_state(target); } else if constexpr (is_history_state(target)) { return get_parent_state(target); } else { return transition.parent(); } } template constexpr auto resolveSrc(Transition transition) { auto constexpr src = transition.source(); if constexpr (is_initial_state(src)) { return get_state(src); } else if constexpr (is_exit_state(src)) { return get_state(src); } else if constexpr (is_direct_state(src)) { return get_state(src); } else { return src; } } template constexpr auto resolveSrcParent(Transition transition) { auto constexpr src = transition.source(); if constexpr (is_exit_state(src)) { return get_parent_state(src); } else if constexpr (is_direct_state(src)) { return get_parent_state(src); } else { return transition.parent(); } } template constexpr auto resolveEntryAction(Transition transition) { if constexpr (transition.internal()) { return [](auto&&...) {}; } else if constexpr (has_entry_action(transition.target())) { return get_entry_action(transition.target()); } else { return [](auto&&...) {}; } } template constexpr auto resolveInitialStateEntryAction(Transition transition) { if constexpr (has_substate_initial_state_entry_action(transition.target())) { return get_entry_action(bh::at_c<0>(collect_initial_states(transition.target()))); } else { return [](auto&&...) {}; } } template constexpr auto resolveExitAction(Transition transition) { const auto hasPseudoExitAction = has_pseudo_exit_action(transition); (void)hasPseudoExitAction; if constexpr (transition.internal()) { return [](auto&&...) {}; } else if constexpr (has_exit_action(transition.source())) { return get_exit_action(transition.source()); } else if constexpr (hasPseudoExitAction) { return get_exit_action(resolveSrcParent(transition)); } else { return [](auto&&...) {}; } } template constexpr auto resolveNoAction(Transition transition) { const auto isNoAction = is_no_action(transition.action()); if constexpr (isNoAction) { return [](auto&&...) {}; } else { return transition.action(); } } template constexpr auto resolveEntryExitAction(Transition transition) { return [exitAction(resolveExitAction(transition)), action(resolveNoAction(transition)), entryAction(resolveEntryAction(transition)), initialStateEntryAction(resolveInitialStateEntryAction(transition))](auto&&... params) { exitAction(std::forward(params)...); action(std::forward(params)...); entryAction(std::forward(params)...); initialStateEntryAction(std::forward(params)...); }; } template constexpr auto resolveAction(Transition transition) { const auto hasAction = has_action(transition); if constexpr (hasAction) { return resolveEntryExitAction(transition); } else { return transition.action(); } } template constexpr auto resolveHistory(Transition transition) { if constexpr (is_history_state(transition.target())) { return true; } else { return false; } } } #include #include #include #include #include #include #include namespace hsm { namespace bh { using namespace boost::hana; } constexpr auto function = bh::second; constexpr auto predicate = bh::first; constexpr auto case_ = bh::make_pair; constexpr auto otherwise = bh::always(bh::true_c); template constexpr auto switch_(Case&&... cases_) { return function(bh::find_if(bh::make_basic_tuple(cases_...), predicate).value()); } constexpr auto lazy_predicate = bh::compose(bh::eval, bh::first); constexpr auto lazy_otherwise = bh::make_lazy(bh::always(bh::true_c)); template constexpr auto lazy_switch_(Case&&... cases_) { return function(bh::find_if(bh::make_basic_tuple(cases_...), lazy_predicate).value()); } } #include #include #include #include #include #include namespace hsm { namespace bh { using namespace boost::hana; } namespace { template constexpr auto flatten_sub_transition_table(State state); } template constexpr auto flatten_transition_table(State state) { auto flattenSubTransitionTable = [state](auto transition) { return bh::prepend( flatten_sub_transition_table(resolveSubStateParent(transition)), details::extended_transition(state, transition)); }; return bh::flatten(bh::transform(make_transition_table2(state), flattenSubTransitionTable)); } namespace { template constexpr auto flatten_sub_transition_table(State state) { // clang-format off if constexpr(has_transition_table(state)){ return flatten_transition_table(state); } else { return bh::make_basic_tuple(); } // clang-format on } } } #include #include #include #include #include namespace hsm { namespace bh { using namespace boost::hana; } namespace { constexpr auto resolveInitialState = [](auto transition) { if constexpr (is_initial_state(transition.source())) { return get_state(transition.source()); } else { return transition.source(); } }; constexpr auto resolveExtentedInitialState = [](auto transition) { if constexpr (is_initial_state(transition.source())) { return get_state(transition.source()); } else { return transition.source(); } }; constexpr auto extractExtendedStateTypeids = [](auto transition) { return bh::make_basic_tuple( bh::typeid_(resolveExtentedInitialState(transition)), bh::typeid_(transition.target())); }; constexpr auto extractExtendedStates = [](auto transition) { return bh::make_basic_tuple(resolveExtentedInitialState(transition), transition.target()); }; const auto extractStates = [](auto transition) { return bh::make_basic_tuple(transition.source(), transition.target()); }; constexpr auto extractStateTypeids = [](auto transition) { return bh::make_basic_tuple( bh::typeid_(resolveInitialState(transition)), bh::typeid_(transition.target())); }; } template constexpr auto collect_child_state_typeids_recursive(State parentState) { auto transitions = flatten_transition_table(parentState); auto collectedStates = bh::flatten(bh::transform(transitions, extractExtendedStateTypeids)); return remove_duplicates(collectedStates); } template constexpr auto collect_child_states_recursive(State parentState) { return bh::flatten(bh::transform(flatten_transition_table(parentState), extractExtendedStates)); ; } template constexpr auto collect_state_typeids_recursive(State parentState) { auto collectedStates = bh::append(collect_child_state_typeids_recursive(parentState), bh::typeid_(parentState)); return collectedStates; } template constexpr auto collect_states_recursive(State parentState) { return remove_duplicates(bh::append(collect_child_states_recursive(parentState), parentState)); } template constexpr auto collect_child_state_typeids(State state) { auto transitions = make_transition_table2(state); auto collectedStates = bh::flatten(bh::transform(transitions, extractStateTypeids)); return remove_duplicates(collectedStates); } template constexpr auto collect_child_states(State state) { return remove_duplicates( bh::flatten(bh::transform(make_transition_table2(state), extractStates))); } } #include #include #include #include #include #include #include #include #include #include namespace hsm { namespace bh { using namespace boost::hana; } /** * Predicate that checks for an empty tuple */ constexpr auto isNotEmpty = [](const auto& tuple) { return bh::not_(bh::equal(bh::size_c<0>, bh::size(tuple))); }; /** * Returns a tuple of internal transitions of a state if it exists. * Otherwise a empty tuple is returned. Source and target of the * transition are set to parentstate as a placeholder and need to * be filled with all child states of the particular state. * * @param state State for which the internal transitions should be returned * */ constexpr auto get_internal_transition_table = [](auto parentState) { if constexpr (has_internal_transition_table(parentState)) { return bh::transform( make_internal_transition_table(parentState), [parentState](auto internalTransition) { return details::extended_transition( parentState, details::transition( parentState, internalTransition.event(), internalTransition.guard(), internalTransition.action(), parentState)); }); } else { return bh::make_basic_tuple(); } }; /** * Extends an internal transitions to all provided states * * @param internalTranstion Internal transition that should be extended * @param states tuple of states */ template constexpr auto extend_internal_transition(Transition internalTransition, States states) { return bh::transform(states, [internalTransition](auto state) { return details::internal_extended_transition( internalTransition.parent(), details::transition( state, internalTransition.event(), internalTransition.guard(), internalTransition.action(), state)); }); } /** * Returns the internal transitions for each for each state * [[transition1, transition2], [transition3, transition4], []] * * @param states a tuple of states */ constexpr auto get_internal_transitions = [](auto states) { return bh::flatten(bh::filter( bh::transform( states, [](auto parentState) { constexpr auto extend = bh::capture(parentState)([](auto parentState, auto transition) { // Folowing lines satisfies older gcc -Werror=unused-but-set-parameter (void)transition; if constexpr (has_transition_table(parentState)) { return extend_internal_transition( transition, collect_child_states(parentState)); } else { return bh::make_basic_tuple(); } }); return bh::transform(get_internal_transition_table(parentState), extend); }), isNotEmpty)); }; /** * Returns a tuple of extended internal transitions reachable from a given rootState * * @param rootState */ template constexpr auto flatten_internal_transition_table(State rootState) { return [](auto states) { return bh::to(bh::flatten(get_internal_transitions(states))); }(collect_states_recursive(rootState)); } } // namespace hsm #include #include #include #include #include namespace hsm { namespace bh { using namespace boost::hana; } namespace { constexpr auto collectEventTypeids = [](auto transition) { return transition.event().typeid_; }; constexpr auto collectEvents = [](auto transition) { using Event = typename decltype(transition.event().typeid_)::type; return bh::tuple_t; }; } template constexpr auto collect_event_typeids_recursive(State state) { return remove_duplicates(bh::transform( bh::concat(flatten_transition_table(state), flatten_internal_transition_table(state)), collectEventTypeids)); } template constexpr auto collect_event_typeids_recursive_from_transitions(TransitionTuple transitions) { return remove_duplicates(bh::transform(transitions, collectEventTypeids)); } template constexpr auto collect_events_recursive(State state) { return remove_duplicate_types(bh::flatten(bh::transform( bh::concat(flatten_transition_table(state), flatten_internal_transition_table(state)), collectEvents))); } } #include namespace hsm::details::utils { template > struct dunique_ptr; template struct dunique_ptr> { dunique_ptr() : ptr(nullptr) { } explicit dunique_ptr(T* ptr) : ptr(ptr) { } dunique_ptr(const dunique_ptr&) = delete; auto operator=(const dunique_ptr&) -> dunique_ptr& = delete; dunique_ptr(dunique_ptr&& other) noexcept : ptr(other.ptr) { other.ptr = nullptr; } auto operator=(dunique_ptr&& other) noexcept -> dunique_ptr& { swap(other); return *this; } ~dunique_ptr() { reset(); } void reset(T* value = nullptr) { T* to_delete = ptr; ptr = value; // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) delete to_delete; } auto release() -> T* { T* result = ptr; ptr = nullptr; return result; } explicit operator bool() const { return bool(ptr); } [[nodiscard]] auto get() const -> T* { return ptr; } auto operator*() const -> T& { return *ptr; } auto operator->() const -> T* { return ptr; } void swap(dunique_ptr& other) { std::swap(ptr, other.ptr); } auto operator==(const dunique_ptr& other) const -> bool { return ptr == other.ptr; } auto operator!=(const dunique_ptr& other) const -> bool { return !(*this == other); } auto operator<(const dunique_ptr& other) const -> bool { return ptr < other.ptr; } auto operator<=(const dunique_ptr& other) const -> bool { return !(other < *this); } auto operator>(const dunique_ptr& other) const -> bool { return other < *this; } auto operator>=(const dunique_ptr& other) const -> bool { return !(*this < other); } private: T* ptr; }; template void swap(dunique_ptr& lhs, dunique_ptr& rhs) { lhs.swap(rhs); } } #include #include #include #include namespace hsm { namespace bh { using namespace boost::hana; } template auto get(std::reference_wrapper ref) -> auto& { return ref.get(); } // NOLINTNEXTLINE(cppcoreguidelines-special-member-functions) template struct IDispatchTableEntry { virtual ~IDispatchTableEntry() = default; virtual void executeAction(Event& event) = 0; virtual auto executeGuard(Event& event) -> bool = 0; }; template < bool Internal, class Action, class Guard, class SourcePtr, class TargetPtr, class Event, class OptionalDependency> class DispatchTableEntry final : public IDispatchTableEntry { public: constexpr DispatchTableEntry( Action action, Guard guard, SourcePtr source, TargetPtr target, OptionalDependency optionalDependency) : m_action(action) , m_guard(guard) , m_source(std::move(source)) , m_target(std::move(target)) , m_optionalDependency(optionalDependency) { } void executeAction(Event& event) override { // clang-format off if constexpr(is_action()){ [](auto& action, auto& event, const auto& source, auto& target, const auto& optionalDependency) { bh::unpack(optionalDependency, [&action, &event, &source, &target](const auto&... optionalDependency){ using Target = typename TargetPtr::element_type::element_type; (void) target; if constexpr(is_default_constructable(bh::type_c)){ if constexpr (Internal){ action(event, **source, **source, get(optionalDependency)...); } else { action(event, **source, **target, get(optionalDependency)...); } } else { action(event, **source, target, get(optionalDependency)...); } }); }(m_action, event, m_source, m_target, m_optionalDependency); } // clang-format on } auto executeGuard(Event& event) -> bool override { // clang-format off if constexpr(is_guard()){ return []( auto& guard, auto& event, const auto& source, const auto& target, const auto& optionalDependency) { return bh::unpack( optionalDependency, [&guard, &event, &source, &target](const auto&... optionalDependency) { (void) target; if constexpr (Internal) { return guard(event, **source, **source, get(optionalDependency)...); } else { return guard(event, **source, **target, get(optionalDependency)...); } }); }(m_guard, event, m_source, m_target, m_optionalDependency); } else { return true; } // clang-format on } private: Action m_action; Guard m_guard; SourcePtr m_source; TargetPtr m_target; OptionalDependency m_optionalDependency; }; template < class Transition, class Action, class Guard, class EventTypeid, class Source, class Target, class Dependency> constexpr auto make_transition( Transition transition, Action action, Guard guard, EventTypeid eventTypeid, Source source, Target target, Dependency optionalDependency) { using Event = typename decltype(eventTypeid)::type; return hsm::details::utils::dunique_ptr>( new DispatchTableEntry< transition.internal(), Action, Guard, Source, Target, Event, decltype(optionalDependency)>(action, guard, source, target, optionalDependency)); } template struct NextState { StateIdx combinedState {}; bool history {}; bool defer {}; bool valid = false; bool internal = false; hsm::details::utils::dunique_ptr> transition; }; } #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace hsm { namespace bh { using namespace boost::hana; } template constexpr auto nParentStates(State rootState) { return bh::length(collect_parent_state_typeids(rootState)); } template constexpr auto nStates(State rootState) { return bh::length(collect_state_typeids_recursive(rootState)); } template constexpr auto nEvents(State rootState) { return bh::length(collect_event_typeids_recursive(rootState)); } template constexpr decltype(auto) hasParallelRegions(State rootState) { return bh::greater(maxInitialStates(rootState), bh::size_c<1>); } template < class State, class DispatchTables, class TransitionTuple, class EventTypeid, class StatesMap, class Dependencies> constexpr auto addDispatchTableEntry( State rootState, DispatchTables& dispatchTables, TransitionTuple&& transition, EventTypeid eventTypeid, StatesMap&& statesMap, Dependencies optionalDependency) { bh::apply( [=, &dispatchTables](auto combinedStateIds, auto source, auto target) { bh::apply( [=, &dispatchTables]( auto fromIdx, auto toIdx, auto history, auto mappedSource, auto mappedTarget) -> void { bh::apply( [=](auto& dispatchTable, auto&& transition2, bool internal) -> void { const auto defer = false; const auto valid = true; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-constant-array-index) dispatchTable[fromIdx].push_front( { toIdx, history, defer, valid, internal, std::move(transition2) }); }, dispatchTables[eventTypeid], make_transition( transition, resolveAction(transition), transition.guard(), eventTypeid, mappedSource, mappedTarget, optionalDependency), transition.internal()); }, getCombinedStateIdx(combinedStateIds, resolveSrcParent(transition), source), getCombinedStateIdx(combinedStateIds, resolveDstParent(transition), target), resolveHistory(transition), bh::find(statesMap, bh::typeid_(source)).value(), bh::find(statesMap, bh::typeid_(target)).value()); }, getCombinedStateTypeids(rootState), resolveSrc(transition), resolveDst(transition)); } template < class State, class DispatchTables, class TransitionTuple, class EventTypeid, class StatesMap, class Dependencies> constexpr auto addDispatchTableEntryOfSubMachineExits( State rootState, DispatchTables& dispatchTables, TransitionTuple transition, EventTypeid eventTypeid, StatesMap&& statesMap, Dependencies optionalDependency) { (void)rootState; (void)eventTypeid; (void)optionalDependency; if constexpr (transition.internal()) { return; } constexpr auto parentState = transition.source(); if constexpr (has_transition_table(parentState)) { bh::for_each(collect_child_state_typeids(parentState), [=, &dispatchTables](auto state) { bh::apply( [=, &dispatchTables](auto combinedStateTypeids, auto target) { bh::apply( [=, &dispatchTables]( auto fromIdx, auto toIdx, auto history, auto mappedParent, auto mappedTarget) { bh::apply( [=](auto& dispatchTable, auto&& transition2, bool internal) { const auto defer = false; const auto valid = true; // TODO: Since dispatch table is static, transitions might be // added twice dispatchTable[fromIdx].push_front({ toIdx, history, defer, valid, internal, std::move(transition2) }); }, dispatchTables[eventTypeid], make_transition( transition, resolveAction(transition), transition.guard(), eventTypeid, mappedParent, mappedTarget, optionalDependency), transition.internal()); }, getCombinedStateIdx(combinedStateTypeids, parentState, state), getCombinedStateIdx( combinedStateTypeids, resolveDstParent(transition), target), resolveHistory(transition), bh::find(statesMap, bh::typeid_(parentState)).value(), bh::find(statesMap, bh::typeid_(target)).value()); }, getCombinedStateTypeids(rootState), resolveDst(transition)); }); } } constexpr auto filter_transitions = [](auto transitions, auto eventTypeid) { auto isEvent = [eventTypeid](auto transition) { return bh::equal(transition.event().typeid_, eventTypeid); }; return bh::filter(transitions, isEvent); }; template < class State, class DispatchTables, class StatesMap, class Dependencies, class TransitionTuple> constexpr auto fill_dispatch_table_with_transitions( State rootState, DispatchTables& dispatchTables, StatesMap&& statesMap, Dependencies&& optionalDependency, TransitionTuple transitions) { auto eventTypeids = collect_event_typeids_recursive_from_transitions(transitions); bh::for_each( eventTypeids, [rootState, &dispatchTables, statesMap, optionalDependency, transitions](auto eventTypeid) { auto filteredTransitions = filter_transitions(transitions, eventTypeid); bh::for_each( filteredTransitions, [rootState, &dispatchTables, statesMap, optionalDependency, eventTypeid]( auto transition) { addDispatchTableEntry( rootState, dispatchTables, transition, eventTypeid, statesMap, optionalDependency); addDispatchTableEntryOfSubMachineExits( rootState, dispatchTables, transition, eventTypeid, statesMap, optionalDependency); }); }); } constexpr auto getDeferingTransitions = [](auto rootState) { constexpr auto transitionHasDeferedEvents = [](auto transition) { return has_deferred_events(resolveExtentedInitialState(transition)); }; constexpr auto transitions = flatten_transition_table(rootState); return bh::filter(transitions, transitionHasDeferedEvents); }; constexpr auto hasDeferedEvents = [](auto rootState) { return bh::not_equal(bh::size_c<0>, bh::size(getDeferingTransitions(rootState))); }; template constexpr auto fill_dispatch_table_with_deferred_events( RootState rootState, DispatchTables& dispatchTables, OptionalDependency /*optionalDependency*/) { const auto transitions = getDeferingTransitions(rootState); bh::for_each(transitions, [=, &dispatchTables](auto transition) { const auto deferredEvents = get_defer_events(resolveExtentedInitialState(transition)); bh::for_each(deferredEvents, [=, &dispatchTables](auto event) { const auto combinedStateTypeids = getCombinedStateTypeids(rootState); auto& dispatchTable = dispatchTables[event]; const auto from = getCombinedStateIdx( combinedStateTypeids, resolveSrcParent(transition), resolveSrc(transition)); dispatchTable[from].push_front({}); dispatchTable[from].front().defer = true; }); }); } template constexpr auto fill_dispatch_table_with_external_transitions( const RootState& rootState, DispatchTables& dispatchTables, StatesMap&& statesMap, OptionalDependency&& optionalDependecy) { fill_dispatch_table_with_transitions( rootState, dispatchTables, statesMap, optionalDependecy, flatten_transition_table(rootState)); } template constexpr auto fill_dispatch_table_with_internal_transitions( const RootState& rootState, DispatchTables& dispatchTables, StatesMap&& statesMap, OptionalDependency&& optionalDependecy) { fill_dispatch_table_with_transitions( rootState, dispatchTables, statesMap, optionalDependecy, flatten_internal_transition_table(rootState)); } } #include #include namespace hsm { namespace bh { using namespace boost::hana; } constexpr auto to_map = [](auto&& tupleOfPairs) { return bh::unpack(std::forward(tupleOfPairs), [](auto&&... pairs) { return bh::make_map(std::forward(pairs)...); }); }; } #include #include #include namespace hsm { namespace bh { using namespace boost::hana; } // NOLINTNEXTLINE(cppcoreguidelines-special-member-functions) template struct IUnexpectedEventHandler { virtual ~IUnexpectedEventHandler() = default; virtual void executeHandler(Event& event) = 0; }; template class UnexpectedEventHandler final : public IUnexpectedEventHandler { public: UnexpectedEventHandler( Handler handler, CurrentStatePtr currentState, OptionalDependency optionalDependency) : m_handler(handler) , m_currentState(std::move(currentState)) , m_optionalDependency(optionalDependency) { } void executeHandler(Event& event) override { bh::unpack(m_optionalDependency, [this, &event](const auto&... optionalDependency) { m_handler(event, **m_currentState, get(optionalDependency)...); }); } private: Handler m_handler; CurrentStatePtr m_currentState; OptionalDependency m_optionalDependency; }; template constexpr auto make_unexpected_event_handler_tables(RootState rootState) { return to_map([rootState](auto eventTypeids) { return bh::transform(eventTypeids, [rootState](auto eventTypeid) { return [eventTypeid](auto states) { using Event = typename decltype(eventTypeid)::type; return bh::make_pair( eventTypeid, std::array< hsm::details::utils::dunique_ptr>, states>()); }(nStates(rootState) * nParentStates(rootState)); }); }(collect_event_typeids_recursive(rootState))); } } #include #include #include namespace hsm { namespace bh { using namespace boost::hana; } template constexpr auto make_unexpected_event_handler( Handler handler, CurrentStatePtr currentStatePtr, EventTypeid eventTypeid, OptionalDependency optionalDependency) { using Event = typename decltype(eventTypeid)::type; return hsm::details::utils::dunique_ptr>( new UnexpectedEventHandler( handler, currentStatePtr, optionalDependency)); } template < class RootState, class StatesMap, class UnexpectedEventHandlerTables, class UnexpectedEventHandler, class OptionalDependency> constexpr auto fill_unexpected_event_handler_tables( RootState rootState, StatesMap statesMap, UnexpectedEventHandlerTables& unexpectedEventHandlerTables, UnexpectedEventHandler unexpectedEventHandler, OptionalDependency optionalDependency) { [=, &unexpectedEventHandlerTables](auto eventTypeids) { bh::for_each(eventTypeids, [=, &unexpectedEventHandlerTables](auto eventTypeid) { bh::for_each( collect_parent_state_typeids(rootState), [=, &unexpectedEventHandlerTables](auto parentStateTypeid) { bh::for_each( collect_state_typeids_recursive(rootState), [=, &unexpectedEventHandlerTables](auto stateTypeid) { [=, &unexpectedEventHandlerTables]( auto combinedStateTypeids, auto mappedCurrentState) { [=, &unexpectedEventHandlerTables](auto combinedStateId) { unexpectedEventHandlerTables[eventTypeid][combinedStateId] = make_unexpected_event_handler( unexpectedEventHandler, mappedCurrentState, eventTypeid, optionalDependency); }(getCombinedStateIdx( combinedStateTypeids, parentStateTypeid, stateTypeid)); }(getCombinedStateTypeids(rootState), bh::find(statesMap, stateTypeid).value()); }); }); }); }(collect_event_typeids_recursive(rootState)); } } #include #include #include namespace hsm { namespace bh { using namespace boost::hana; } template constexpr auto make_dispatch_tables(RootState rootState) { return to_map([rootState](auto eventTypeids) { return bh::transform(eventTypeids, [rootState](auto eventTypeid) { return [eventTypeid](auto states) { using Event = typename decltype(eventTypeid)::type; return bh::make_pair( eventTypeid, std::array>, states>()); }(nStates(rootState) * nParentStates(rootState)); }); }(collect_event_typeids_recursive(rootState))); } } #include #include namespace hsm { namespace bh { using namespace boost::hana; } /*** * Returns a map from typeid(state) -> State * of all states found recursive under parentState * */ template constexpr auto make_states_map(State&& parentState) { auto collectedStateTypeids = collect_state_typeids_recursive(parentState); auto collectedStatesPtr = bh::transform(collect_states_recursive(parentState), unwrap_typeid_to_shared_ptr); return to_map(to_pairs(bh::zip(collectedStateTypeids, collectedStatesPtr))); } } #include #include #include namespace hsm { namespace bh { using namespace boost::hana; } const auto is_anonymous_transition = [](auto transition) { return bh::typeid_(transition.event()) == bh::typeid_(none {}); }; const auto is_history_transition = [](auto transition) { return is_history_state(transition.target()); }; template constexpr auto has_anonymous_transition(State rootState) { auto transitions = flatten_transition_table(rootState); auto anonymousTransition = bh::filter(transitions, is_anonymous_transition); return bh::not_equal(bh::size_c<0>, bh::size(anonymousTransition)); } template constexpr auto has_history(State rootState) { auto transitions = flatten_transition_table(rootState); auto historyTransitions = bh::filter(transitions, is_history_transition); return bh::not_equal(bh::size_c<0>, bh::size(historyTransitions)); } } #include #include #include #include #include namespace hsm { namespace { template auto constexpr make_variant_queue(EventsTuple events) { using Variant_t = typename decltype(boost::hana::unpack(events, boost::hana::template_))::type; return std::queue {}; } } template class variant_queue { EventsTuple m_events; using Queue_t = decltype(make_variant_queue(m_events)); Queue_t m_queue; public: variant_queue(const EventsTuple& events) : m_events(events){ } [[nodiscard]] auto empty() const -> bool{ return m_queue.empty(); } [[nodiscard]] auto size() const -> std::size_t { return m_queue.size(); } template void push(const T& e) { m_queue.push(e); } template void push(T&& e) { m_queue.push(e); } template void visit(const Callable& callable){ if(empty()){ throw std::runtime_error("variant_queue is empty"); } auto frontElement = m_queue.front(); m_queue.pop(); std::visit([&callable](auto&& arg){ callable(arg); }, frontElement); } }; } #include #include #include #include #include #include #include #include namespace hsm { namespace bh { using namespace boost::hana; } template class sm { static constexpr state_t rootState {}; using Region = std::uint8_t; using Events = decltype(collect_event_typeids_recursive(rootState)); using StatesMap = decltype(make_states_map(rootState)); using DispatchTables = decltype(make_dispatch_tables(rootState)); using UnexpectedEventHandlerTables = decltype(make_unexpected_event_handler_tables(rootState)); std::array m_currentCombinedState; std::array, nParentStates(rootState)> m_initial_states; std::array, nParentStates(rootState)> m_history; variant_queue m_defer_queue; std::size_t m_currentRegions {}; StatesMap m_statesMap; DispatchTables m_dispatchTables; UnexpectedEventHandlerTables m_unexpectedEventHandlerTables; public: sm(OptionalParameters&... optionalParameters) : m_initial_states() , m_history() , m_defer_queue(collect_event_typeids_recursive(rootState)) , m_statesMap(make_states_map(rootState)) { static_assert( has_transition_table(rootState), "Root state has no make_transition_table method"); static_assert( bh::size(flatten_transition_table(rootState)), "Transition table needs at least one transition"); static_assert( maxInitialStates(rootState), "Transition table needs to have at least one initial state"); auto optionalDependency = bh::make_basic_tuple(std::ref(optionalParameters)...); if constexpr (has_unexpected_event_handler(rootState)) { fill_unexpected_event_handler_tables( rootState, m_statesMap, m_unexpectedEventHandlerTables, get_unexpected_event_handler(rootState), optionalDependency); } fill_dispatch_table(optionalDependency); fill_initial_state_table(rootState, m_initial_states); fill_initial_state_table(rootState, m_history); init_current_state(); update_current_regions(); } template auto process_event(Event&& event) { static_assert( bh::contains( collect_event_typeids_recursive(rootState), bh::type_c::type>), "Processed event was not found in the transition table"); if (!process_event_internal(event)) { call_unexpected_event_handler(event); return; } process_deferred_events(); } template auto is(State state) -> bool { return currentState(0) == getStateIdx(rootState, state); } template auto is(ParentState parentState, State state) -> bool { return currentParentState() == getParentStateIdx(rootState, parentState) && currentState(0) == getStateIdx(rootState, state); } template auto is(Region region, ParentState parentState, State state) -> bool { return currentParentState() == getParentStateIdx(rootState, parentState) && currentState(region) == getStateIdx(rootState, state); } template auto parent_is(ParentState parentState) -> bool { return currentParentState() == getParentStateIdx(rootState, parentState); } auto status() -> std::string { std::stringstream statusStream; const auto currentRegions = current_regions(); for (Region region = 0; region < currentRegions; region++) { statusStream << "[" << region << "] " << "combined: " << m_currentCombinedState[region] << " " << "parent: " << currentParentState() << " " << "state: " << currentState(region); } return statusStream.str(); } auto set_dependency(OptionalParameters&... optionalParameters) { auto optionalDependency = bh::make_basic_tuple(std::ref(optionalParameters)...); fill_dispatch_table(optionalDependency); } private: template auto process_event_internal(Event&& event) -> bool { bool allGuardsFailed = true; bool allTransitionsInvalid = true; const auto currentRegions = current_regions(); for (Region region = 0; region < currentRegions; region++) { // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-constant-array-index) auto& results = get_dispatch_table_entry(event, region); if (results.empty()) { continue; } for (auto& result : results) { if (result.defer) { m_defer_queue.push(event); return true; } if (!result.transition->executeGuard(event)) { allTransitionsInvalid = false; continue; } allTransitionsInvalid = false; allGuardsFailed = false; update_current_state(region, result); result.transition->executeAction(event); break; } } if (allTransitionsInvalid) { return false; } if (allGuardsFailed) { return true; } process_anonymous_transitions(); return true; } auto process_deferred_events() { if constexpr (hasDeferedEvents(rootState)) { if (!m_defer_queue.empty()) { m_defer_queue.visit([this](auto event) { this->process_event_internal(event); }); } } } auto process_anonymous_transitions() { if constexpr (has_anonymous_transition(rootState)) { while (true) { auto allGuardsFailed = true; const auto currentRegions = current_regions(); for (Region region = 0; region < currentRegions; region++) { auto event = noneEvent {}; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-constant-array-index) auto& results = get_dispatch_table_entry(event, region); if (results.empty()) { return; } for (auto& result : results) { if (!result.transition->executeGuard(event)) { continue; } update_current_state(region, result); result.transition->executeAction(event); allGuardsFailed = false; break; } } if (allGuardsFailed) { return; } } } } template constexpr auto dispatch_table_at(StateIdx index, const Event event) -> auto& { return m_dispatchTables[event][index]; } template void update_current_state(Region region, const DispatchTableEntry& dispatchTableEntry) { if (dispatchTableEntry.internal) { return; } if constexpr (has_history(rootState)) { // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-constant-array-index) m_history[currentParentState()][region] = m_currentCombinedState[region]; if (dispatchTableEntry.history) { auto parent = calcParentStateIdx(nStates(rootState), dispatchTableEntry.combinedState); // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-constant-array-index) auto combined = m_history[parent][region]; // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-constant-array-index) m_currentCombinedState[region] = combined; } else { // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-constant-array-index) m_currentCombinedState[region] = dispatchTableEntry.combinedState; } } else { // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-constant-array-index) m_currentCombinedState[region] = dispatchTableEntry.combinedState; } update_current_regions(); } void update_current_regions() { if constexpr (hasParallelRegions(rootState)) { // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-constant-array-index) m_currentRegions = m_initial_states[currentParentState()].size(); } } auto current_regions() -> std::size_t { if constexpr (hasParallelRegions(rootState)) { return m_currentRegions; } else { return 1; } } template auto call_unexpected_event_handler(Event& event) { if constexpr (has_unexpected_event_handler(rootState)) { // TODO: What todo in a multi region state machine? m_unexpectedEventHandlerTables[bh::typeid_(event)] .at(m_currentCombinedState.at(0)) ->executeHandler(event); } } auto currentState(Region region) { return calcStateIdx(nStates(rootState), m_currentCombinedState.at(region)); } auto currentParentState() { return calcParentStateIdx(nStates(rootState), m_currentCombinedState[0]); } void init_current_state() { const auto initialParentState = getParentStateIdx(rootState, rootState); for (Region region = 0; region < m_initial_states[initialParentState].size(); region++) { // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-constant-array-index) m_currentCombinedState[region] = m_initial_states[initialParentState][region]; } } template constexpr auto get_dispatch_table_entry(Event& event, Region region) -> decltype(auto) { // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-constant-array-index) return m_dispatchTables[bh::typeid_(event)][m_currentCombinedState[region]]; } template void fill_dispatch_table(OptionalDependency& optionalDependency) { fill_dispatch_table_with_internal_transitions( rootState, m_dispatchTables, m_statesMap, optionalDependency); fill_dispatch_table_with_external_transitions( rootState, m_dispatchTables, m_statesMap, optionalDependency); fill_dispatch_table_with_deferred_events(rootState, m_dispatchTables, optionalDependency); } }; } namespace hsm { template struct StateBase { using type = Type; template constexpr auto operator+(const event_t&) const { return TransitionSE> {}; } template constexpr auto operator+(const TransitionEG& transition) const { return TransitionSEG { transition.guard }; } template constexpr auto operator+(const TransitionEA& transition) const { return TransitionSEA { transition.action }; } template constexpr auto operator+(const TransitionEGA& transition) const { return TransitionSEGA { transition.guard, transition.action }; } template constexpr auto operator/(const Action& action) const { return TransitionSA { action }; } template constexpr auto operator[](const Guard& guard) const { return TransitionSG { guard }; } // NOLINTNEXTLINE(cppcoreguidelines-c-copy-assignment-signature) template constexpr auto operator=(const Target& target) const { return details::transition( state_t {}, event_t {}, noGuard {}, noAction {}, target); } template constexpr auto operator<=(const TransitionSE&) const { return details::transition( state_t {}, Event {}, noGuard {}, noAction {}, state_t {}); } template constexpr auto operator<=(const TransitionSEG& transitionSeg) const { return details::transition( state_t {}, Event {}, transitionSeg.guard, noAction {}, state_t {}); } template constexpr auto operator<=(const TransitionSEA& transitionSea) const { return details::transition( state_t {}, Event {}, noGuard {}, transitionSea.action, state_t {}); } template constexpr auto operator<=(const TransitionSEGA& transitionSega) const { return details::transition( state_t {}, Event {}, transitionSega.guard, transitionSega.action, state_t {}); } template constexpr auto operator<=(const state_t& source) const { return details::transition( source, event_t {}, noGuard {}, noAction {}, state_t {}); } template auto operator==(OtherState) const -> bool { return boost::hana::equal( boost::hana::type_c, boost::hana::type_c); } }; template struct state_t : public StateBase { using StateBase::operator=; constexpr auto operator*() const { return initial_t {}; } }; template const state_t state {}; template struct initial_t : public StateBase>> { using StateBase>>::operator=; }; template const initial_t initial {}; template struct direct_t : public StateBase, state_t>> { using StateBase, state_t>>::operator=; }; template const direct_t direct {}; template struct entry_t : public StateBase, state_t>> { using StateBase, state_t>>::operator=; }; template const entry_t entry {}; template struct exit_t : public StateBase, state_t>> { using StateBase, state_t>>::operator=; }; template const exit_t exit {}; template struct history_t : public StateBase>> { using StateBase>>::operator=; }; template const history_t history {}; } #include namespace hsm { namespace bh { using namespace boost::hana; } constexpr auto transition_table = bh::make_basic_tuple; template constexpr auto events = bh::tuple_t; } #include #include namespace hsm { template class TransitionEGA { public: constexpr TransitionEGA(const Guard& guard, const Action& action) : guard(guard) , action(action) { } constexpr auto operator+() { return details::internal_transition(Event {}, guard, action); } public: const Guard guard; const Action action; }; template class TransitionEG { public: constexpr TransitionEG(const Guard& guard) : guard(guard) { } template constexpr auto operator/(const Action& action) { return TransitionEGA { guard, action }; } constexpr auto operator+() { return details::internal_transition(Event {}, guard, noAction {}); } public: const Guard guard; }; template class TransitionEA { public: constexpr TransitionEA(const Action& action) : action(action) { } constexpr auto operator+() { return details::internal_transition(Event {}, noGuard {}, action); } public: const Action action; }; template class TransitionSEGA { public: constexpr TransitionSEGA(const Guard& guard, const Action& action) : guard(guard) , action(action) { } // NOLINTNEXTLINE(cppcoreguidelines-c-copy-assignment-signature) template constexpr auto operator=(const Target& target) { return details::transition(state_t {}, Event {}, guard, action, target); } const Guard guard; const Action action; }; template class TransitionSEG { public: constexpr TransitionSEG(const Guard& guard) : guard(guard) { } // NOLINTNEXTLINE(cppcoreguidelines-c-copy-assignment-signature) template constexpr auto operator=(const Target& target) { return details::transition(state_t {}, Event {}, guard, noAction {}, target); } const Guard guard; }; template class TransitionSEA { public: constexpr TransitionSEA(const Action& action) : action(action) { } // NOLINTNEXTLINE(cppcoreguidelines-c-copy-assignment-signature) template constexpr auto operator=(const Target& target) { return details::transition(state_t {}, Event {}, noGuard {}, action, target); } const Action action; }; template class TransitionSE { public: // NOLINTNEXTLINE(cppcoreguidelines-c-copy-assignment-signature) template constexpr auto operator=(const Target& target) { return details::transition(state_t {}, Event {}, noGuard {}, noAction {}, target); } }; template class TransitionSA { public: constexpr TransitionSA(const Action& action) : action(action) { } // NOLINTNEXTLINE(cppcoreguidelines-c-copy-assignment-signature) template constexpr auto operator=(const Target& target) { return details::transition( state_t {}, event_t {}, noGuard {}, action, target); } private: const Action action; }; template class TransitionSGA { public: constexpr TransitionSGA(const Guard& guard, const Action& action) : guard(guard) , action(action) { } // NOLINTNEXTLINE(cppcoreguidelines-c-copy-assignment-signature) template constexpr auto operator=(const Target& target) { return details::transition( state_t {}, event_t {}, guard, action, target); } private: const Guard guard; const Action action; }; template class TransitionSG { public: constexpr TransitionSG(const Guard& guard) : guard(guard) { } // NOLINTNEXTLINE(cppcoreguidelines-c-copy-assignment-signature) template constexpr auto operator=(const Target& target) { return details::transition( state_t {}, event_t {}, guard, noAction {}, target); } template constexpr auto operator/(const Action& action) { return TransitionSGA { guard, action }; } private: const Guard guard; }; } namespace hsm { constexpr auto transition = details::transition; constexpr auto internal_transition = details::internal_transition; }