diff --git a/lib/include/srslte/common/choice_type.h b/lib/include/srslte/common/choice_type.h index 6e9417319..514b00002 100644 --- a/lib/include/srslte/common/choice_type.h +++ b/lib/include/srslte/common/choice_type.h @@ -310,11 +310,16 @@ template class choice_t : private choice_details::tagged_union_t { using base_t = choice_details::tagged_union_t; + +public: + //! Useful metafunction template using enable_if_can_hold = typename std::enable_if::type>()>::type; + template + using disable_if_can_hold = + typename std::enable_if::type>()>::type; -public: using base_t::can_hold_type; using base_t::get; using base_t::get_if; @@ -408,6 +413,12 @@ const T* get_if(const choice_t& c) return c.template get_if(); } +template +T& get(choice_t& c) +{ + return c.template get(); +} + template auto get(const choice_t& c) -> decltype(c.template get()) { diff --git a/lib/include/srslte/common/fsm.h b/lib/include/srslte/common/fsm.h index 33aa5c77e..c415825b1 100644 --- a/lib/include/srslte/common/fsm.h +++ b/lib/include/srslte/common/fsm.h @@ -57,13 +57,25 @@ struct variant_convert { { static_assert(not std::is_same::type, typename std::decay::type>::value, "State cannot transition to itself.\n"); + if (p != nullptr) { + srslte::get(*v).exit(); + } *v = std::move(s); + srslte::get(*v).enter(); } TargetVariant* v; PrevState* p; }; struct fsm_helper { + //! Metafunction to determine if FSM can hold given State type + template + using get_fsm_state_list = decltype(std::declval().states); + template + using enable_if_fsm_state = typename get_fsm_state_list::template enable_if_can_hold; + template + using disable_if_fsm_state = typename get_fsm_state_list::template disable_if_can_hold; + //! Stayed in same state template static void handle_state_change(FSM* f, same_state* s, PrevState* p) @@ -79,17 +91,25 @@ struct fsm_helper { } //! Simple state transition in FSM template - static auto handle_state_change(FSM* f, State* s, PrevState* p) -> decltype(f->states = std::move(*s), void()) + static enable_if_fsm_state handle_state_change(FSM* f, State* s, PrevState* p) { static_assert(not std::is_same::value, "State cannot transition to itself.\n"); + if (p != nullptr) { + srslte::get(f->states).exit(); + } f->states = std::move(*s); + srslte::get(f->states).enter(); } //! State not present in current FSM. Attempt state transition in parent FSM in the case of NestedFSM - template - static void handle_state_change(FSM* f, Args&&... args) + template + static disable_if_fsm_state handle_state_change(FSM* f, State* s, PrevState* p) { static_assert(FSM::is_nested, "State is not present in the FSM list of valid states"); - handle_state_change(f->parent_fsm()->derived(), args...); + if (p != nullptr) { + srslte::get(f->states).exit(); + p = nullptr; + } + handle_state_change(f->parent_fsm()->derived(), s, p); } //! Trigger Event, that will result in a state transition @@ -140,10 +160,12 @@ class state_t public: state_t() = default; virtual const char* name() const = 0; + virtual void enter() {} + virtual void exit() {} }; template -class fsm_t +class fsm_t : public state_t { public: // get access to derived protected members from the base @@ -155,8 +177,6 @@ public: }; static const bool is_nested = false; - virtual const char* name() const = 0; - // Push Events to FSM template void trigger(Ev&& e) diff --git a/lib/test/common/choice_type_test.cc b/lib/test/common/choice_type_test.cc index dde9cdc61..930592e5c 100644 --- a/lib/test/common/choice_type_test.cc +++ b/lib/test/common/choice_type_test.cc @@ -77,6 +77,10 @@ static_assert(std::is_same::default_type, char static_assert(tagged_union_t::can_hold_type(), "Can hold type implementation is incorrect\n"); static_assert(not tagged_union_t::can_hold_type(), "Can hold type implementation is incorrect\n"); +static_assert(std::is_same::enable_if_can_hold, void>::value, + "Metafunction enable if not working\n"); +static_assert(std::is_same::disable_if_can_hold, void>::value, + "Metafunction enable if not working\n"); } // namespace choice_details } // namespace srslte diff --git a/lib/test/common/fsm_test.cc b/lib/test/common/fsm_test.cc index fe48d0cd9..7294bf013 100644 --- a/lib/test/common/fsm_test.cc +++ b/lib/test/common/fsm_test.cc @@ -38,18 +38,13 @@ public: uint32_t idle_enter_counter = 0, state1_enter_counter = 0; uint32_t foo_counter = 0; - fsm1() : states(idle_st{this}) {} - const char* name() const override { return "fsm1"; } // idle state - struct idle_st : srslte::state_t { - idle_st(fsm1* f) - { - test_log->info("fsm1::%s::enter called\n", name()); - f->idle_enter_counter++; - } - ~idle_st() { test_log->info("fsm1::%s::exit called\n", name()); } + struct idle_st : public srslte::state_t { + idle_st(fsm1* f) { f->idle_enter_counter++; } + void enter() final { test_log->info("fsm1::%s::enter called\n", name()); } + void exit() final { test_log->info("fsm1::%s::exit called\n", name()); } const char* name() const final { return "idle"; } }; @@ -72,7 +67,7 @@ public: struct state_inner : public srslte::state_t { const char* name() const final { return "state_inner"; } state_inner() { test_log->info("fsm2::%s::enter called\n", name()); } - ~state_inner() { test_log->info("fsm2::%s::exit called\n", name()); } + void exit() final { test_log->info("fsm2::%s::exit called\n", name()); } }; fsm2(fsm1* f_) : nested_fsm_t(f_) { test_log->info("%s::enter called\n", name()); } @@ -130,6 +125,24 @@ auto fsm1::react(state1& s, ev2 e) -> srslte::state_list return idle_st{this}; } +// Static Checks + +namespace srslte { +namespace fsm_details { + +static_assert(std::is_same, + srslte::state_list >::value, + "get state list failed\n"); +static_assert(std::is_same, void>::value, + "get state list failed\n"); +static_assert(std::is_same, void>::value, + "get state list failed\n"); + +} // namespace fsm_details +} // namespace srslte + +// Runtime checks + int test_hsm() { test_log->set_level(srslte::LOG_LEVEL_INFO);