diff --git a/cxx/mcc_fsm.h b/cxx/mcc_fsm.h index a11080f..76a8bbc 100644 --- a/cxx/mcc_fsm.h +++ b/cxx/mcc_fsm.h @@ -5,7 +5,10 @@ /* FINITE-STATE MACHINE IMPLEMENTATION */ +#include #include +#include +#include #include "mcc_fsm_utils.h" @@ -13,6 +16,8 @@ namespace mcc::fsm { +class MccFsmAbstractState; + namespace traits { @@ -23,13 +28,254 @@ concept fsm_event_c = }; template -concept fsm_state_c = - std::is_default_constructible_v && std::is_move_constructible_v && std::movable && requires { - [](std::type_identity) {}(T::transition_t); +concept fsm_event_state_pair_c = requires { + [] StT>(std::type_identity>) { + }(std::type_identity()); +}; - { T::ID } -> std::same_as; // static constant member of state identificator + +template +concept fsm_event_state_pair_tuple_c = + requires { [](std::type_identity>) {}(std::type_identity()); }; + +} // namespace traits + + +/* + A base FSM state class with implementation of "event -- new state" transition table +*/ +class MccFsmAbstractState +{ + static constexpr std::string_view ID = "MCC-FSM-STATE"; + +protected: + MccFsmAbstractState() = default; + + // helpers + template + struct in_tuple; + + template + struct in_tuple> : std::disjunction...> { + }; + + template + static constexpr bool in_tpl_v = in_tuple::value; + + // template + // struct in_pair : std::disjunction...> { + // }; + + template + static constexpr bool in_pair_v = std::disjunction...>::value; + // static constexpr bool in_pair_v = in_pair::value; + + /* transition table definition */ + +public: + template + struct trans_table_t; + + template + struct trans_table_t { + using events_t = std::tuple; + using states_t = std::tuple; + using unique_states_t = states_t; + + using evst_pairs_t = std::tuple; + + static constexpr bool unique = true; + + template + using find_state_by_event_t = + std::conditional_t, typename PT::first_second, std::nullptr_t>; + }; + + template + struct trans_table_t { + private: + using ev_t = typename PT::first_type; + using st_t = typename PT::second_type; + + // static constexpr bool non_unique = in_pair_v; + static constexpr bool non_unique = (std::same_as || ...); + + public: + using events_t = + std::conditional_t::events_t, + decltype(std::tuple_cat(std::declval>(), + std::declval::events_t>()))>; + using states_t = + std::conditional_t::states_t, + decltype(std::tuple_cat(std::declval>(), + std::declval::states_t>()))>; + + using unique_states_t = std::conditional_t< + non_unique, + typename trans_table_t::unique_states_t, + std::conditional_t<(std::same_as || ...), + typename trans_table_t::unique_states_t, + decltype(std::tuple_cat( + std::declval>(), + std::declval::unique_states_t>()))>>; + + using evst_pairs_t = + std::conditional_t::evst_pairs_t, + decltype(std::tuple_cat(std::declval>(), + std::declval::evst_pairs_t>()))>; + + private: + template + struct find_state_by_event { + template + struct helper { + static constexpr size_t idx = + std::same_as> ? I : helper::idx; + }; + + // to stop unwind the recursive 'helper' template + // in std::conditional_t trait + template + struct helper - 1, T> { + static constexpr size_t idx = std::tuple_size_v - 1; + }; + + using res_t = std:: + conditional_t, std::tuple_element_t::idx, states_t>, std::nullptr_t>; + }; + + public: + template + using find_state_by_event_t = find_state_by_event::res_t; + }; // end of 'trans_table_t' +}; // end of 'MccFsmAbstractState' + + +namespace traits +{ + +template +concept fsm_trans_table_c = requires { + [](std::type_identity>) { + }(std::type_identity()); +}; + + +template +concept fsm_state_c = + std::is_default_constructible_v && std::is_move_constructible_v && std::movable && + std::derived_from && requires { + // state class must define a type 'transition_t' which must be a full specialization of + // MccFsmAbstractState::trans_table_t class + [](std::type_identity) {}(std::type_identity()); }; } // namespace traits + + +class MccFiniteStateMachine +{ +public: + template + MccFiniteStateMachine(ST initial_state) : _currentStateID(ST::ID), _currentStateIDX(typeid(ST)) + { + // setStateFunc(); + initFromInitial(typeid(ST)); + } + + template + void dispatchEvent(EvT event) + { + enter_state_func_umap_t[this][_currentStateIDX](std::move(event)); + // state_func_umap_t[this][_currentStateIDX](std::move(event)); + } + +protected: + template + using state_func_t = std::unordered_map>; + + template + inline static std::unordered_map> state_func_umap_t{}; + + template + inline static std::unordered_map> enter_state_func_umap_t{}; + + template + inline static std::unordered_map> exit_state_func_umap_t{}; + + std::string_view _currentStateID; + std::type_index _currentStateIDX; + + std::function _exitCurrentStateFunc; + + template + consteval static void initFromInitial(std::type_index state_idx) + { + initFromInitial(state_idx); + + if constexpr (sizeof...(PTs)) { + initFromInitial(state_idx); + } + } + + template + consteval static void initFromInitial(std::type_index state_idx) + { + using ev_t = typename PT::first_type; + using st_t = typename PT::second_type; + + enter_state_func_umap_t[][state_idx] = [](ev_t event) { + st_t state; + + initFromInitial(st_t::ID); + + if constexpr (requires(st_t inst) { + { inst.enter(std::declval()) }; + }) { + state.enter(std::move(event)); + } else { + state.enter(); + } + }; + } + + + // template + // void setStateFunc() + // { + // setStateFunc(); + + // if constexpr (sizeof...(PTs)) { + // setStateFunc(); + // } + // } + + + // template + // void setStateFunc() + // { + // using ev_t = typename PT::first_type; + // using st_t = typename PT::second_type; + + // state_func_umap_t[this][_currentStateIDX] = [](ev_t event) { + // st_t state; + + // if constexpr (requires(st_t inst) { + // { inst.enter(std::declval()) }; + // }) { + // state.enter(std::move(event)); + // } else { + // state.enter(); + // } + // }; + // } + + template + struct merge_tuples_t; +}; + } // namespace mcc::fsm