hreintke
hreintke

Reputation: 51

Call BaseState function instead of Derived function when using std::variant<...>

I am experimenting with finate state machines and std::variant<..>

std::variant will hold all possible states The states will be defined in State classes.

What I wanted was to have a BaseState class, in which "generic events" should be handled. With derived State classes handling specific states.

struct LightOn;
struct LightOff;

struct Toggle;
struct Reset;

using State = std::variant<LightOn,LightOff>;
using Event = std::variant<Toggle,Reset>;

struct BaseState
{
    void on_enter(){Serial.printf("BaseState enter\r\n");};
    void on_event (const Reset &r);
};

struct LightOn : BaseState
{
    LightOn(){Serial.printf("LightOn constructor\r\n");};
    void on_event(const Event &e);
    void on_exit(){Serial.printf("LightOn exit\r\n");}
    void on_enter(){Serial.printf("LightOn enter\r\n");}
};

struct LightOff : BaseState
{
    LightOff(){Serial.printf("LightOff constructor\r\n");};
    void on_event(const Event &e);
    void on_exit(){Serial.printf("LightOff exit\r\n");}
};

struct Toggle
{
    Toggle(){Serial.printf("Struct Toggle\r\n");};
};

struct Reset
{
    Reset(){Serial.printf("Struct Reset\r\n");};
};

void LightOn::on_event(const Event &e){Serial.printf("LightOn event\r\n");};
void LightOff::on_event(const Event &e){Serial.printf("LightOff event\r\n");};
void BaseState::on_event(const Reset &r){Serial.printf("BaseState reset\r\n");};

The question I have is in the on_event function.

There is a specific one on Reset in the Basestate void on_event(const Reset &r); And a more generic one in LightOn and LightOff void on_event(const Event &e); (using Event = std::variant<Toggle,Reset>;)

But when I do

LightOn n;
n.on_event(Reset{}); 

The on_event from LightOn is called, not the one from BaseState.

Is there a way to achieve : Call BaseState function when a BaseState function is available for this specific event ?

Upvotes: 0

Views: 53

Answers (1)

Maksymilian Mika
Maksymilian Mika

Reputation: 144

Similar thing was answered in this question.

The name lookup stops at the sub class (because there is a match there).

You can use using to introduce the function from the base class (BaseState) into the same scope. (This will make it follow the usual overloading rules, in this case picking the exact match, which is the function from the base class.)

struct LightOn;
struct LightOff;

struct Toggle;
struct Reset;

using State = std::variant<LightOn,LightOff>;
using Event = std::variant<Toggle,Reset>;

struct BaseState
{
    void on_enter(){Serial.printf("BaseState enter\r\n");};
    void on_event (const Reset &r);
};

struct LightOn : public BaseState
{
    LightOn(){Serial.printf("LightOn constructor\r\n");};
    using BaseState::on_event; // <-- Added to the scope
    void on_event(const Event &e); 
    void on_exit(){Serial.printf("LightOn exit\r\n");}
    void on_enter(){Serial.printf("LightOn enter\r\n");}
};

struct LightOff : public BaseState
{
    LightOff(){Serial.printf("LightOff constructor\r\n");};
    void on_event(const Event &e);
    void on_exit(){Serial.printf("LightOff exit\r\n");}
};

struct Toggle
{
    Toggle(){Serial.printf("Struct Toggle\r\n");};
};

struct Reset
{
    Reset(){Serial.printf("Struct Reset\r\n");};
};

void LightOn::on_event(const Event &e){Serial.printf("LightOn event\r\n");};
void LightOff::on_event(const Event &e){Serial.printf("LightOff event\r\n");};
void BaseState::on_event(const Reset &r){Serial.printf("BaseState reset\r\n");};

Also, take a look at Boost compile-time FSM generator example. It seems to accomplish the same goal as yours.

Upvotes: 1

Related Questions