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