Skip to content

Usage issue with an object containing all the actions and guards as member functions. #11

@phelter

Description

@phelter

With state machines I'd like to encapsulate the state machine within an object - after a bunch of investigation and trial and error with sml and sml2 found the following - works best: Something along the lines of this comment: boost-ext/sml#311 (comment)

The issue I have is when I try and use lambda's

eg:

#include <sml2>
#include <iostream>
#include <chrono>
#include <fmt/chrono.h>
#include <fmt/format.h>
#include <utility>
#include <thread>

namespace phs = std::placeholders;

struct Wait
{
    std::chrono::seconds duration;
};
struct Cancel
{
};
struct Expire
{
};

static std::string formatTimepoint(const std::chrono::system_clock::time_point &timePoint)
{
    // Convert time_point to time_t
    std::time_t time = std::chrono::system_clock::to_time_t(timePoint);

    // Convert time_t to tm as local time
    std::tm tm = *std::localtime(&time);

    // Format the tm structure to a string using fmt::format
    return fmt::format("{:%Y-%m-%d %H:%M:%S}", tm);
}

class TEBase {
public:
    virtual ~TEBase() = default;
    virtual void start(const Wait &ev) = 0;
};

struct SmCtx
{
    SmCtx(TEBase &t)
        : te(t)
    {}

    auto operator()()
    {
        using namespace sml;
        using namespace sml::dsl;

        // auto start = [this](const auto &ev) -> void { te.start(ev); }; // Fails
        // auto start = [&](const auto &ev) -> void { te.start(ev); }; // Fails
        auto start = std::bind(&TEBase::start, &te, phs::_1);

        return transition_table{*"idle"_s + event<Wait> / start = "waiting"_s};
    }

    TEBase &te;
};

class TE : TEBase {
public:
    TE()
        : m_sm(SmCtx{*this})
    {}
    ~TE() override = default;

    void run() { m_sm.process_event(Wait{std::chrono::seconds(1)}); }

    void start(const Wait &ev) override
    {
        std::cout << "Hello World at: " << formatTimepoint(std::chrono::system_clock::now()) << std::endl;
        std::this_thread::sleep_for(ev.duration);
        std::cout << "Just woke up at: " << formatTimepoint(std::chrono::system_clock::now()) << std::endl;
    }

    sml::sm<SmCtx> m_sm;
};

int main()
{
    TE te;
    te.run();
    return 0;
}

The lambda's don't work since there is a difference in how the lambda captures this vs the std::bind captures this. Both will compile but when using lambda's there's a seg-fault when it tries to call the action that is the lambda.

Would be great if there's a compile-time check that a lambda isn't usable.

Also if concepts could be used to provide much easier to understand error identification when things like this are an issue. The amount of cruff in the compile warnings are problematic for static debugging of the issues related to the state machine.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions