Skip to content

Commit

Permalink
plausible simplified approach
Browse files Browse the repository at this point in the history
  • Loading branch information
msandstedt committed Nov 26, 2021
1 parent 1006308 commit 93227b1
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 76 deletions.
75 changes: 19 additions & 56 deletions src/lib/support/StateMachine.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,12 @@ struct VariantState : Variant<Ts...>
{

private:
template <typename T>
void Enter()
template <typename T, typename E>
void Enter(Optional<E> & event)
{
if (chip::Variant<Ts...>::template Is<T>())
{
chip::Variant<Ts...>::template Get<T>().Enter();
event = chip::Variant<Ts...>::template Get<T>().Enter();
}
}

Expand Down Expand Up @@ -93,9 +93,12 @@ struct VariantState : Variant<Ts...>
return instance;
}

void Enter()
template <typename E>
Optional<E> Enter()
{
[](...) {}((this->template Enter<Ts>(), 0)...);
Optional<E> event;
[](...) {}((this->template Enter<Ts, E>(event), 0)...);
return event;
}

void Exit()
Expand All @@ -116,25 +119,6 @@ struct VariantState : Variant<Ts...>
}
};

/**
* The interface for dispatching events into the State Machine.
* @tparam TEvent a variant holding the Events for the State Machine.
*/
template <typename TEvent>
class Context
{
public:
virtual ~Context() = default;

/**
* Dispatch an event to the current state.
* @note This call can result in the current state being deleted. Do not
* access current state memory after calling this method.
* @param evt a variant holding an Event for the State Machine.
*/
virtual void Dispatch(const TEvent & evt) = 0;
};

/**
* This is a functional approach to the State Machine design pattern. The design is
* borrowed from http://www.vishalchovatiya.com/state-design-pattern-in-modern-cpp
Expand Down Expand Up @@ -203,48 +187,27 @@ class Context
* @tparam TTransitions an object that implements the () operator for transitions.
*/
template <typename TState, typename TEvent, typename TTransitions>
class StateMachine : public Context<TEvent>
class StateMachine
{
public:
StateMachine(TTransitions & tr) : mCurrentState(tr.GetInitState()), mTransitions(tr) {}
~StateMachine() override = default;
void Dispatch(const TEvent & evt) override
{
auto inProcess = !events.empty();
events.push_back(evt);
if (!inProcess)
{
HandleEvents();
}
}

TState mCurrentState;

private:
void HandleEvents()
void Dispatch(const TEvent & evt)
{
while (!events.empty())
Optional<TEvent> optEvent(evt);
Optional<TState> optState;
while (optEvent.HasValue() && (optState = mTransitions(mCurrentState, evt)).HasValue())
{
auto count = events.size();
auto optState = mTransitions(mCurrentState, events.front());
if (optState.HasValue())
{
auto newState = optState.Value();
newState.LogTransition(mCurrentState.GetName());
mCurrentState.Exit();
mCurrentState = newState;
while (events.size() > count)
{
events.pop_back(); // events discarded per design
}
mCurrentState.Enter();
}
events.pop_front();
auto newState = optState.Value();
newState.LogTransition(mCurrentState.GetName());
mCurrentState.Exit();
mCurrentState = newState;
optEvent = mCurrentState.template Enter<TEvent>();
}
}

TState mCurrentState;
TTransitions & mTransitions;
std::deque<TEvent> events{};
};

} // namespace StateMachine
Expand Down
28 changes: 8 additions & 20 deletions src/lib/support/tests/TestStateMachine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ struct Event4
};

using Event = chip::Variant<Event1, Event2, Event3, Event4>;
using Context = chip::StateMachine::Context<Event>;

struct MockState
{
Expand All @@ -45,7 +44,7 @@ struct MockState
unsigned mLogged;
const char * mPrevious;

void Enter() { ++mEntered; }
chip::StateMachine::Optional<Event> Enter() { ++mEntered; return {}; }
void Exit() { ++mExited; }
void LogTransition(const char * previous)
{
Expand All @@ -56,46 +55,40 @@ struct MockState

struct BaseState
{
void Enter() { mMock.Enter(); }
chip::StateMachine::Optional<Event> Enter() { return mMock.Enter(); }
void Exit() { mMock.Exit(); }
void LogTransition(const char * previous) { mMock.LogTransition(previous); }
const char * GetName() { return mName; }

chip::StateMachine::Context<Event> & mCtx;
const char * mName;
MockState & mMock;
};

struct State1 : public BaseState
{
State1(Context & ctx, const char * name, MockState & mock) : BaseState{ ctx, name, mock } {}
State1(MockState & mock) : BaseState{ "State2", mock } {}
};

struct State2 : public BaseState
{
State2(Context & ctx, const char * name, MockState & mock) : BaseState{ ctx, name, mock } {}
State2(MockState & mock) : BaseState{ "State2", mock } {}
};

using State = chip::StateMachine::VariantState<State1, State2>;

struct StateFactory
{
Context & mCtx;
MockState ms1{ 0, 0, 0, nullptr };
MockState ms2{ 0, 0, 0, nullptr };

StateFactory(Context & ctx) : mCtx(ctx) {}
auto CreateState1() { return State::Create<State1>(ms1); }

auto CreateState1() { return State::Create<State1>(mCtx, "State1", ms1); }

auto CreateState2() { return State::Create<State2>(mCtx, "State2", ms2); }
auto CreateState2() { return State::Create<State2>(ms2); }
};

struct Transitions
{
Context & mCtx;
StateFactory mFactory;
Transitions(Context & ctx) : mCtx(ctx), mFactory(ctx) {}

using OptState = chip::StateMachine::Optional<State>;
State GetInitState() { return mFactory.CreateState1(); }
Expand All @@ -111,15 +104,10 @@ struct Transitions
}
else if (state.Is<State1>() && event.Is<Event4>())
{
// legal - Dispatches event without transition
mCtx.Dispatch(Event::Create<Event2>());
return {};
return mFactory.CreateState2();
}
else if (state.Is<State2>() && event.Is<Event4>())
{
// illegal - Returned Transition will cause events
// dispatched from the transitions table to be ignored.
mCtx.Dispatch(Event::Create<Event2>());
return mFactory.CreateState1();
}
else
Expand All @@ -135,7 +123,7 @@ class SimpleStateMachine
Transitions mTransitions;
chip::StateMachine::StateMachine<State, Event, Transitions> mStateMachine;

SimpleStateMachine() : mTransitions(mStateMachine), mStateMachine(mTransitions) {}
SimpleStateMachine() : mStateMachine(mTransitions) {}
~SimpleStateMachine() {}
};

Expand Down

0 comments on commit 93227b1

Please sign in to comment.