diff --git a/cxx/mcc_finite_state_machine.h b/cxx/mcc_finite_state_machine.h index 9f84361..f1bf457 100644 --- a/cxx/mcc_finite_state_machine.h +++ b/cxx/mcc_finite_state_machine.h @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -14,6 +15,64 @@ namespace mcc::fsm { +enum class MccFiniteStateMachineErrorCode : int { ERROR_OK, ERROR_UNREGISTERED_EVENT_TYPE, ERROR_UNHANDLED_TRANSITION }; + +} // namespace mcc::fsm + + +namespace std +{ +template <> +class is_error_code_enum : public true_type +{ +}; +} // namespace std + + +namespace mcc::fsm +{ + + +// error category +struct MccFiniteStateMachineCategory : public std::error_category { + MccFiniteStateMachineCategory() : std::error_category() {} + + const char* name() const noexcept + { + return "ADC_GENERIC_DEVICE"; + } + + std::string message(int ec) const + { + MccFiniteStateMachineErrorCode err = static_cast(ec); + + switch (err) { + case MccFiniteStateMachineErrorCode::ERROR_OK: + return "OK"; + case MccFiniteStateMachineErrorCode::ERROR_UNREGISTERED_EVENT_TYPE: + return "unregistered event type"; + case MccFiniteStateMachineErrorCode::ERROR_UNHANDLED_TRANSITION: + return "unhandled transition"; + default: + return "UNKNOWN"; + } + } + + static const MccFiniteStateMachineCategory& get() + { + static const MccFiniteStateMachineCategory constInst; + return constInst; + } +}; + + +inline std::error_code make_error_code(MccFiniteStateMachineErrorCode ec) +{ + return std::error_code(static_cast(ec), MccFiniteStateMachineCategory::get()); +} + + + namespace traits { @@ -221,26 +280,64 @@ protected: using variant_from_tuple_t = typename variant_from_tuple::variant_t; - // - template + // call given function for all event-types in the given std::tuple + template static void for_each_event(FT&& func, std::index_sequence) { (func(std::get(EvTplT{})), ...); }; - template + template static void for_each_event(FT&& func) { for_each_event(std::forward(func), std::make_index_sequence>()); }; + + template + static void for_each_event_in_each_state(FT&& func) + { + for_each_event(std::forward(func)); + } + + + template + static void for_each_event_in_each_state(FT&& func, std::index_sequence) + { + (for_each_event_in_each_state>(std::forward(func)), ...); + } + + template + static void for_each_event_in_each_state(FT&& func) + { + for_each_event_in_each_state(std::forward(func), + std::make_index_sequence>()); + } + + + // check if given event-type is in std::tuple of event-types + template + struct in_tuple; + + template + struct in_tuple> : std::disjunction...> { + }; + + template + static constexpr bool in_tuple_v = in_tuple::value; + + template inline static std::unordered_map> _dispatchEventFunc{}; std::vector> _moveFunc{}; std::vector> _copyFunc{}; - std::vector> _destroyFunc{}; + std::vector> _destroyFunc{}; + + std::string_view _currentStateID; + + std::mutex _transitionMutex; static MccFiniteStateMachine& copyInstance(const MccFiniteStateMachine* from, MccFiniteStateMachine* to) { @@ -249,6 +346,7 @@ protected: func(from, to); } + to->_currentStateID = from->_currentStateID; to->_moveFunc = from->_moveFunc; to->_copyFunc = from->_copyFunc; to->_destroyFunc = from->_destroyFunc; @@ -264,6 +362,7 @@ protected: func(from, to); } + to->_currentStateID = std::move(from->_currentStateID); to->_moveFunc = std::move(from->_moveFunc); to->_copyFunc = std::move(from->_copyFunc); to->_destroyFunc = std::move(from->_destroyFunc); @@ -274,7 +373,7 @@ protected: public: template - MccFiniteStateMachine(InitStateT) + MccFiniteStateMachine(InitStateT) : _currentStateID(InitStateT::ID) { using states_t = deduce_states_t; auto states = std::make_shared(); @@ -308,15 +407,23 @@ public: // setup dispatch event functions - for_each_event>([states, currentState, this](EvT) mutable { + + using all_events_t = deduce_events_t; + for_each_event([states, currentState, this](EvT) mutable { + if constexpr (!in_tuple_v) { + throw std::system_error(MccFiniteStateMachineErrorCode::ERROR_UNREGISTERED_EVENT_TYPE); + } + _dispatchEventFunc[this] = [states, currentState, this](EvT& event) mutable { + std::lock_guard lock(_transitionMutex); + std::visit( - [&event, states, currentState](curr_state_t*) { + [&event, states, currentState, this](curr_state_t*) { using to_state_t = curr_state_t::transition_t::template find_state_by_event_t; if constexpr (!std::is_void_v) { // exit from current if constexpr (requires(curr_state_t inst) { - { inst.exit(std::declval()) }; + { inst.exit(std::declval()) }; }) { std::get(*states).exit(event); } else if constexpr (requires(curr_state_t inst) { @@ -327,23 +434,26 @@ public: // transit ... if constexpr (requires(EvT inst) { - { inst.on_transit() }; + { inst.onTransit() }; }) { - event.on_transit(); + event.onTransit(); } - *currentState = std::get(*states); + *currentState = &std::get(*states); + _currentStateID = to_state_t::ID; // enter to new if constexpr (requires(to_state_t inst) { - { inst.enter(std::declval()) }; + { inst.enter(std::declval()) }; }) { - std::get(*states).enter(event); + std::get(*states).enter(event); } else if constexpr (requires(to_state_t inst) { { inst.enter() }; }) { - std::get(*states).enter(); + std::get(*states).enter(); } + } else { + throw std::system_error(MccFiniteStateMachineErrorCode::ERROR_UNHANDLED_TRANSITION); } }, *currentState); @@ -355,25 +465,39 @@ public: _copyFunc.emplace_back([](const MccFiniteStateMachine* from, MccFiniteStateMachine* to) { _dispatchEventFunc[to] = _dispatchEventFunc[from]; }); - _destroyFunc.emplace_back([](MccFiniteStateMachine* inst) { + _destroyFunc.emplace_back([](const MccFiniteStateMachine* inst) { // _dispatchEventFunc.erase(inst); }); }); + + std::cout << "MOVE VEC: " << _moveFunc.size() << "\n"; } - MccFiniteStateMachine(const MccFiniteStateMachine& other) { copyInstance(&other, this); } + MccFiniteStateMachine(const MccFiniteStateMachine& other) + { + copyInstance(&other, this); + } - MccFiniteStateMachine(MccFiniteStateMachine&& other) { moveInstance(&other, this); } + MccFiniteStateMachine(MccFiniteStateMachine&& other) + { + moveInstance(&other, this); + } - MccFiniteStateMachine& operator=(const MccFiniteStateMachine& other) { return copyInstance(&other, this); } + MccFiniteStateMachine& operator=(const MccFiniteStateMachine& other) + { + return copyInstance(&other, this); + } - MccFiniteStateMachine& operator=(MccFiniteStateMachine&& other) { return moveInstance(&other, this); } + MccFiniteStateMachine& operator=(MccFiniteStateMachine&& other) + { + return moveInstance(&other, this); + } virtual ~MccFiniteStateMachine() { for (auto& func : _destroyFunc) { - func(); + func(this); } } @@ -382,6 +506,18 @@ public: { _dispatchEventFunc[this](event); } + + template + auto dispatchEvent() + { + static EvT event; + return dispatchEvent(event); + } + + std::string_view currentStateID() const + { + return _currentStateID; + } }; diff --git a/cxx/tests/fsm_test.cpp b/cxx/tests/fsm_test.cpp index fb0a4e8..5d9f312 100644 --- a/cxx/tests/fsm_test.cpp +++ b/cxx/tests/fsm_test.cpp @@ -15,9 +15,11 @@ struct S { struct EV1 { static constexpr std::string_view ID = "EV1"; - // EV1() = default; - // EV1(EV1&&) = default; - // EV1& operator=(EV1&&) = default; + void onTransit() + { + // + std::cout << "EV1::onTransit()\n"; + } }; struct EV2 { @@ -44,12 +46,22 @@ struct ST3 { struct ST2 { static constexpr std::string_view ID = "ST2"; using transition_t = fsm::fsm_transition_table_t, std::pair>; + + void enter(EV1& ev) + { + std::cout << "transit to " << ID << "-state\n"; + } }; struct ST1 { static constexpr std::string_view ID = "ST1"; using transition_t = fsm::fsm_transition_table_t, std::pair, std::pair, std::pair>; + + void exit() + { + std::cout << "transit from " << ID << "-state\n"; + } }; // struct STN : fsm::MccFsmAbstractState { @@ -124,7 +136,21 @@ int main() */ - fsm::MccFiniteStateMachine(ST1{}); + fsm::MccFiniteStateMachine fsmach(ST1{}); + + fsmach.dispatchEvent(); + // fsmach.dispatchEvent(); + + + // using tab_t = fsm::fsm_transition_table_t, std::pair>; + + // using st_t = tab_t::find_state_by_event_t; + + // int status; + // char* aa = abi::__cxa_demangle(typeid(st_t).name(), NULL, NULL, &status); + // std::cout << "aa = " << aa << "\n"; + // free(aa); + return 0; }