getsoubl
getsoubl

Reputation: 1055

How to pass a variant as constructor parameter or as function argument

I am reading about implementation of state machines using variants. I try to create a construction that take a variant parameter to initialize the state. However whent the constructor is defined , I get a warning that this is recognized as function declaration.

Moreover when I try to define a setter for the state, compiler error is generated when try to call this

Here is the code

#include "pch.h"
#include <iostream>
#include <variant>
#include <cassert>

struct DoorState
{
    struct DoorOpened {};
    struct DoorClosed {};
    struct DoorLocked {};

    using State = std::variant<DoorOpened, DoorClosed, DoorLocked>;
    DoorState()
    {
    }
    DoorState(State & state)
    {
        m_state = state;
    }
    void open()
    {
        m_state = std::visit(OpenEvent{}, m_state);
    }

    void close()
    {
        m_state = std::visit(CloseEvent{}, m_state);
    }

    void lock()
    {
        m_state = std::visit(LockEvent{}, m_state);
    }

    void unlock()
    {
        m_state = std::visit(UnlockEvent{}, m_state);
    }

    struct OpenEvent
    {
        State operator()(const DoorOpened&) { return DoorOpened(); }
        State operator()(const DoorClosed&) { return DoorOpened(); }
        // cannot open locked doors
        State operator()(const DoorLocked&) { return DoorLocked(); }
    };

    struct CloseEvent
    {
        State operator()(const DoorOpened&) { return DoorClosed(); }
        State operator()(const DoorClosed&) { return DoorClosed(); }
        State operator()(const DoorLocked&) { return DoorLocked(); }
    };

    struct LockEvent
    {
        // cannot lock opened doors
        State operator()(const DoorOpened&) 
        { 
            std::cout << "DoorOpened" << std::endl;
            return DoorOpened(); 
        }
        State operator()(const DoorClosed&) { return DoorLocked(); }
        State operator()(const DoorLocked&) { return DoorLocked(); }
    };

    struct UnlockEvent
    {
        // cannot unlock opened doors
        State operator()(const DoorOpened&) { return DoorOpened(); }
        State operator()(const DoorClosed&) { return DoorClosed(); }
        // unlock
        State operator()(const DoorLocked&) { return DoorClosed(); }
    };
    void set(State state)
    {
    }
    State m_state;
};

int main()
{
    //DoorState s(DoorState::DoorOpened);
    DoorState s; 
    s.set(DoorState::DoorOpened);
    s.lock();
    return 0;
}

Upvotes: 2

Views: 4109

Answers (1)

In

s.set(DoorState::DoorOpened);

you are passing a type, you should pass an instance of a type, try

s.set(DoorState::DoorOpened{});

after this change I was able to compile in MSVC 2019 (16.1.3)

Edit: This is an edit to address Scheff and Jarod24 comments, there would have been a case of most vexing parse if we were to uncomment the constructor and write

DoorState s(DoorState::DoorOpened());

This could have been fixed using the uniform initialization syntax, see e.g. https://arne-mertz.de/2015/07/new-c-features-uniform-initialization-and-initializer_list/

DoorState s{DoorState::DoorOpened{}};

This would have solved the most vexing parse issue but created a new issue: DoorState::DoorOpened{}would have been a temporary and it could have never been bound to the input argument of the ctor:

DoorState(State& state)

we then would have needed to change it to

DoorState(const State& state)

Thanks again to Jarod and Scheff for pointing out the issue.

Upvotes: 2

Related Questions