|
|
@ -66,30 +66,30 @@ struct variant_convert {
|
|
|
|
struct fsm_helper {
|
|
|
|
struct fsm_helper {
|
|
|
|
//! Stayed in same state
|
|
|
|
//! Stayed in same state
|
|
|
|
template <typename FSM, typename PrevState>
|
|
|
|
template <typename FSM, typename PrevState>
|
|
|
|
static void handle_state_transition(FSM* f, same_state s, PrevState* p)
|
|
|
|
static void handle_state_change(FSM* f, same_state* s, PrevState* p)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
// do nothing
|
|
|
|
// do nothing
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//! TargetState is type-erased. Apply its stored type to the fsm current state
|
|
|
|
//! TargetState is type-erased. Apply its stored type to the fsm current state
|
|
|
|
template <typename FSM, typename... Args, typename PrevState>
|
|
|
|
template <typename FSM, typename... Args, typename PrevState>
|
|
|
|
static void handle_state_transition(FSM* f, choice_t<Args...>& s, PrevState* p)
|
|
|
|
static void handle_state_change(FSM* f, choice_t<Args...>* s, PrevState* p)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
fsm_details::variant_convert<decltype(f->states), PrevState> visitor{.v = &f->states, .p = p};
|
|
|
|
fsm_details::variant_convert<decltype(f->states), PrevState> visitor{.v = &f->states, .p = p};
|
|
|
|
s.visit(visitor);
|
|
|
|
s->visit(visitor);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//! Simple state transition in FSM
|
|
|
|
//! Simple state transition in FSM
|
|
|
|
template <typename FSM, typename State, typename PrevState>
|
|
|
|
template <typename FSM, typename State, typename PrevState>
|
|
|
|
static auto handle_state_transition(FSM* f, State& s, PrevState* p) -> decltype(f->states = s, void())
|
|
|
|
static auto handle_state_change(FSM* f, State* s, PrevState* p) -> decltype(f->states = std::move(*s), void())
|
|
|
|
{
|
|
|
|
{
|
|
|
|
static_assert(not std::is_same<State, PrevState>::value, "State cannot transition to itself.\n");
|
|
|
|
static_assert(not std::is_same<State, PrevState>::value, "State cannot transition to itself.\n");
|
|
|
|
f->states = s;
|
|
|
|
f->states = std::move(*s);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//! State not present in current FSM. Attempt state transition in parent FSM in the case of NestedFSM
|
|
|
|
//! State not present in current FSM. Attempt state transition in parent FSM in the case of NestedFSM
|
|
|
|
template <typename FSM, typename... Args>
|
|
|
|
template <typename FSM, typename... Args>
|
|
|
|
static void handle_state_transition(FSM* f, Args&&... args)
|
|
|
|
static void handle_state_change(FSM* f, Args&&... args)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
static_assert(FSM::is_nested, "State is not present in the FSM list of valid states");
|
|
|
|
static_assert(FSM::is_nested, "State is not present in the FSM list of valid states");
|
|
|
|
handle_state_transition(f->parent_fsm()->derived(), args...);
|
|
|
|
handle_state_change(f->parent_fsm()->derived(), args...);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//! Trigger Event, that will result in a state transition
|
|
|
|
//! Trigger Event, that will result in a state transition
|
|
|
@ -97,34 +97,35 @@ struct fsm_helper {
|
|
|
|
struct trigger_visitor {
|
|
|
|
struct trigger_visitor {
|
|
|
|
trigger_visitor(FSM* f_, Event&& ev_) : f(f_), ev(std::forward<Event>(ev_)) {}
|
|
|
|
trigger_visitor(FSM* f_, Event&& ev_) : f(f_), ev(std::forward<Event>(ev_)) {}
|
|
|
|
|
|
|
|
|
|
|
|
template <typename State>
|
|
|
|
//! Trigger visitor callback for the current state
|
|
|
|
void operator()(State& s)
|
|
|
|
template <typename CurrentState>
|
|
|
|
|
|
|
|
void operator()(CurrentState& s)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
call_trigger(s);
|
|
|
|
call_trigger(&s);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//! Compute next state type
|
|
|
|
template <typename State>
|
|
|
|
template <typename State>
|
|
|
|
using NextState = decltype(std::declval<FSM>().react(std::declval<State&>(), std::declval<Event>()));
|
|
|
|
using NextState = decltype(std::declval<FSM>().react(std::declval<State&>(), std::declval<Event>()));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//! In case a react(CurrentState&, Event) method is found
|
|
|
|
template <typename State>
|
|
|
|
template <typename State>
|
|
|
|
auto call_trigger(State& s) -> NextState<State>
|
|
|
|
auto call_trigger(State* current_state) -> NextState<State>
|
|
|
|
{
|
|
|
|
{
|
|
|
|
using next_state = NextState<State>;
|
|
|
|
using next_state = NextState<State>;
|
|
|
|
static_assert(not std::is_same<next_state, State>::value, "State cannot transition to itself.\n");
|
|
|
|
static_assert(not std::is_same<next_state, State>::value, "State cannot transition to itself.\n");
|
|
|
|
auto target_state = f->react(s, std::move(ev));
|
|
|
|
auto target_state = f->react(*current_state, std::move(ev));
|
|
|
|
fsm_helper::handle_state_transition(f, target_state, &s);
|
|
|
|
fsm_helper::handle_state_change(f, &target_state, current_state);
|
|
|
|
return target_state;
|
|
|
|
return target_state;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//! In case a react(CurrentState&, Event) method is not found, but we are in a NestedFSM with a trigger method
|
|
|
|
template <typename State>
|
|
|
|
template <typename State>
|
|
|
|
auto call_trigger(State& s) -> decltype(std::declval<State>().trigger(std::declval<Event>()))
|
|
|
|
auto call_trigger(State* s) -> decltype(std::declval<State>().trigger(std::declval<Event>()))
|
|
|
|
{
|
|
|
|
|
|
|
|
s.trigger(std::move(ev));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
same_state call_trigger(...)
|
|
|
|
|
|
|
|
{
|
|
|
|
{
|
|
|
|
// do nothing if no react was found
|
|
|
|
s->trigger(std::move(ev));
|
|
|
|
return same_state{};
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//! No trigger or react method found. Do nothing
|
|
|
|
|
|
|
|
void call_trigger(...) {}
|
|
|
|
|
|
|
|
|
|
|
|
FSM* f;
|
|
|
|
FSM* f;
|
|
|
|
Event ev;
|
|
|
|
Event ev;
|
|
|
@ -158,7 +159,6 @@ public:
|
|
|
|
using Derived::react;
|
|
|
|
using Derived::react;
|
|
|
|
using Derived::states;
|
|
|
|
using Derived::states;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static const bool is_nested = false;
|
|
|
|
static const bool is_nested = false;
|
|
|
|
|
|
|
|
|
|
|
|
virtual const char* name() const = 0;
|
|
|
|
virtual const char* name() const = 0;
|
|
|
@ -167,7 +167,8 @@ public:
|
|
|
|
template <typename Ev>
|
|
|
|
template <typename Ev>
|
|
|
|
void trigger(Ev&& e)
|
|
|
|
void trigger(Ev&& e)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
fwd_trigger(std::forward<Ev>(e));
|
|
|
|
fsm_details::fsm_helper::trigger_visitor<derived_view, Ev> visitor{derived(), std::forward<Ev>(e)};
|
|
|
|
|
|
|
|
derived()->states.visit(visitor);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template <typename State>
|
|
|
|
template <typename State>
|
|
|
@ -192,20 +193,6 @@ public:
|
|
|
|
protected:
|
|
|
|
protected:
|
|
|
|
friend struct fsm_details::fsm_helper;
|
|
|
|
friend struct fsm_details::fsm_helper;
|
|
|
|
|
|
|
|
|
|
|
|
// Forward an event to FSM states and handle transition return
|
|
|
|
|
|
|
|
template <typename Ev>
|
|
|
|
|
|
|
|
void fwd_trigger(Ev&& e)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
fsm_details::fsm_helper::trigger_visitor<derived_view, Ev> visitor{derived(), std::forward<Ev>(e)};
|
|
|
|
|
|
|
|
derived()->states.visit(visitor);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template <typename State>
|
|
|
|
|
|
|
|
void change_state(State& s)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
derived()->states = std::move(s);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Access to CRTP derived class
|
|
|
|
// Access to CRTP derived class
|
|
|
|
derived_view* derived() { return static_cast<derived_view*>(this); }
|
|
|
|
derived_view* derived() { return static_cast<derived_view*>(this); }
|
|
|
|
const derived_view* derived() const { return static_cast<const derived_view*>(this); }
|
|
|
|
const derived_view* derived() const { return static_cast<const derived_view*>(this); }
|
|
|
@ -228,7 +215,6 @@ public:
|
|
|
|
parent_t* parent_fsm() { return fsm_ptr; }
|
|
|
|
parent_t* parent_fsm() { return fsm_ptr; }
|
|
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
protected:
|
|
|
|
friend struct fsm_details::fsm_helper;
|
|
|
|
|
|
|
|
using parent_fsm_t = ParentFSM;
|
|
|
|
using parent_fsm_t = ParentFSM;
|
|
|
|
|
|
|
|
|
|
|
|
ParentFSM* fsm_ptr = nullptr;
|
|
|
|
ParentFSM* fsm_ptr = nullptr;
|
|
|
|