sml icon indicating copy to clipboard operation
sml copied to clipboard

Is it possible to use movable-only state machine with SML?

Open zamazan4ik opened this issue 5 years ago • 3 comments

Hi! Consider the following example:

#include <boost/sml.hpp>

#include <cassert>
#include <iostream>
#include <variant>
#include <mutex>

namespace sml = boost::sml;

namespace {
struct connect {};
struct disconnect {};

class data {
    struct Disconnected {};
    struct Connected {
        int id{};
    };

public:
    explicit data(const std::string& address) : address{address} {}

    inline auto operator()() {
        using namespace boost::sml;
        return make_transition_table(
                * state<Disconnected> + event<connect> / [this] {
                    data_ = Connected{42};
                } = state<Connected>
                , state<Connected> + event<disconnect> / [this] {
                    std::cout << std::get<Connected>(data_).id << std::endl;
                } = X
        );
    }

private:
    std::mutex mutex;
    std::string address{};
    std::variant<Disconnected, Connected> data_{};
};
}  // namespace

int main() {
    sml::sm<data> sm{data{"127.0.0.1"}};
    sm.process_event(connect{});
    sm.process_event(disconnect{});
    assert(sm.is(sml::X));
}

If you try to compile: you will get something like this: https://pastebin.com/rYbdrbL8

I cannot be compiled since std::mutex is movable only type. My question is: is it possible avoid such limitation with SML library? If yes, how can I do it?

zamazan4ik avatar May 01 '20 12:05 zamazan4ik

std::mutex is neither copyable nor movable.

madf avatar May 01 '20 15:05 madf

Sorry, my bad. Of course you are right. What about the following sample?

#include <boost/sml.hpp>

#include <cassert>
#include <iostream>
#include <variant>
#include <mutex>

namespace sml = boost::sml;

namespace {
struct connect {};
struct disconnect {};

struct type
{
    type() = default;
    type(const type&) = delete;
    type(type&&) = default;

    int a;
};

class data {
    struct Disconnected {};
    struct Connected {
        int id{};
    };

public:
    explicit data(const std::string& address) : address{address} {}

    inline auto operator()() {
        using namespace boost::sml;
        return make_transition_table(
                * state<Disconnected> + event<connect> / [this] {
                    data_ = Connected{42};
                } = state<Connected>
                , state<Connected> + event<disconnect> / [this] {
                    std::cout << std::get<Connected>(data_).id << std::endl;
                } = X
        );
    }

private:
    type t;
    std::string address{};
    std::variant<Disconnected, Connected> data_{};
};
}  // namespace

int main() {
    sml::sm<data> sm{data{"127.0.0.1"}};
    sm.process_event(connect{});
    sm.process_event(disconnect{});
    assert(sm.is(sml::X));
}

zamazan4ik avatar May 01 '20 15:05 zamazan4ik

Should probably work fine with perfect forwarding used in aux::pool and aux::pool_type, but I'm not familiar enough with SML guts to be 100% sure. You may try to add it here:

explicit pool_type(T object) : value{object} {}

And here:

explicit pool(Ts... ts) : pool_type<Ts>(ts)... {}

Maybe it would also require making aux::pool movable itself.

madf avatar May 01 '20 21:05 madf