statemachine using variadic template overloading
I am trying to build a statemachine in C++ using variadic templates.
class Event {};
template<typename... TEvents>
class StateMachineActionHandler
{
public:
void action() = delete;
};
template<typename TEvent, typename... TEventRest>
class StateMachineActionHandler<TEvent, TEventRest...> : public StateMachineActionHandler<TEventRest...>
{
public:
virtual void action(const TEvent& event)
{
std::cout << "default" << std::endl;
}
using StateMachineActionHandler<TEventRest...>::action;
};
template <class ...TEvents>
class State : public StateMachineActionHandler<TEvents...>
{
public:
virtual void enterAction() = 0;
virtual void exitAction() = 0;
};
template <class ...TStates>
class StateMachine
{
public:
StateMachine()
{
m_cur = std::get<0>(m_states);
std::visit([](auto state){state.enterAction();}, m_cur);
}
virtual ~StateMachine() = default;
template<typename TEvent>
void handleEvent(const TEvent& event)
{
std::visit([&event] (auto state){ state.action(event);}, m_cur);
}
private:
std::tuple<TStates...> m_states;
std::variant<TStates...> m_cur;
};
Then I create a class that implements this statemachine. However for StateA
I do not override the function void action(const EventB& event)
, since I want it to have the empty default implementation that is provided in the templates.
class EventA : public Event {};
class EventB : public Event {};
class StateA : public State<EventA, EventB>
{
public:
void action(const EventA& event) override { std::cout << "new A A" << std::endl;}
void enterAction() override {std::cout << "enter A" << std::endl; }
void exitAction() override {std::cout << "exit A" << std::endl; }
};
class StateB : public State<EventA, EventB>
{
public:
void action(const EventA& event) override { std::cout << "new B A" << std::endl;}
void action(const EventB& event) override { std::cout << "new B B" << std::endl;}
void enterAction() override {std::cout << "enter B" << std::endl; }
void exitAction() override {std::cout << "exit B" << std::endl; }
};
class StateC : public State<EventA, EventB>
{
public:
void action(const EventA& event) override { std::cout << "new C A" << std::endl;}
void action(const EventB& event) override { std::cout << "new C B" << std::endl;}
void enterAction() override {std::cout << "enter C" << std::endl; }
void exitAction() override {std::cout << "exit C" << std::endl; }
};
class StateMachineTest : public StateMachine<StateA, StateB, StateC>
{
};
Now if I use this class I get an compilation error:
int main()
{
StateMachineTest testSM;
EventB eventB;
testSM.handleEvent(eventB);
}
error: cannot convert ‘const EventB’ to ‘const EventA&’
Why does it not see the empty default implementation?
Solution 1:
You need to use using
-declaration to introduce the default implementation into the derived class definition
class StateA : public State<EventA, EventB>
{
public:
using State<EventA, EventB>::action;
void action(const EventA& event) override { std::cout << "new A A" << std::endl;}
void enterAction() override {std::cout << "enter A" << std::endl; }
void exitAction() override {std::cout << "exit A" << std::endl; }
};
Demo