[Boost :: ext] .SML:是否可以存储回调以稍后处理事件?

问题描述

我来自Boost MSM,现在正在尝试使用SML 1.1.3实现相同的状态机。 使用SML,我无权访问状态机本身,而我不得不处理注入的依赖项。我也大量使用D-Bus,现在在进行异步D-Bus调用时遇到问题。异步D-Bus调用是在注入依赖项的方法中进行的。 D-Bus调用完成后,将调用依赖项中的回调。在那里,我需要一种向状态机发出事件的方法。 存储 PASS src/stackoverflow/64386858/app.spec.js 64386858 ✓ should pass (17ms) console.log node_modules/jest-environment-jsdom/node_modules/jest-mock/build/index.js:866 verified successfully. Implement next logic ----------|----------|----------|----------|----------|-------------------| File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s | ----------|----------|----------|----------|----------|-------------------| All files | 100 | 50 | 100 | 100 | | app.js | 100 | 50 | 100 | 100 | 13 | ----------|----------|----------|----------|----------|-------------------| Test Suites: 1 passed,1 total Tests: 1 passed,1 total Snapshots: 0 total Time: 5.087s,estimated 11s 无效,我不知道异步D-Bus调用结束后它是否仍然有效。

示例:

sml::back::process

...

    auto queryDBusAction = []( Dep& dep,sml::back::process<Ev_Result> processEvent ) {
        dep.makeAsyncDBusCall( SomeCallback );
    };

解决方法

为了访问状态机本身,您需要将应用程序类分为基类和子类。 基类具有纯虚拟成员函数声明。 您可以从sml过渡表操作中调用成员函数。

您需要在过渡表定义之后定义子类。 现在,子类可以访问过渡表的整个定义。 这意味着您可以包含sml::sm作为子类的成员

因此,您可以从成员函数中调用sml后端的process_event()函数。参见memfun1()

#include <iostream>
#include <cassert>

#include <boost/sml.hpp>
#include <boost/asio.hpp>

namespace sml = boost::sml;

struct e1 {};
struct e2 {};

// Separate member function declarations as the base class
struct app_base {
    virtual void memfun1() = 0;
    virtual void memfun2(int) const = 0;
    virtual ~app_base() = default;
};
    
struct app_table {
    auto operator()() const noexcept {
        using namespace sml;
        // I can write member function call in action thanks to base class app_base
        return make_transition_table(
            // source event      guard                     action                                    target
            *"s1"_s + event<e1>  [([] { return true; })] / [](app_base& appb)  { appb.memfun1(); }   = "s2"_s,"s2"_s + event<e2>                          / [](app_base& appb)  { appb.memfun2(42); } = "s1"_s
        );
    }
};

struct app : app_base {
    app(boost::asio::io_context& ioc):ioc { ioc } {}
    
    // post asynchronous callback function that calls process_event()
    void memfun1() override {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
        boost::asio::post(
            ioc,[this] { 
                std::cout << "async callback is called. call process_event()" << std::endl;
                sm_.process_event(e2{});

                using namespace sml;
                assert(sm_.is("s1"_s));
            }
        );
    }
    void memfun2(int v) const override {
        std::cout << __PRETTY_FUNCTION__ << ":" << v << std::endl;
    }

    // state machine backend can be a member of application class
    sml::sm<app_table> sm_ { static_cast<app_base&>(*this) };
    boost::asio::io_context& ioc;
};

int main() {
    using namespace sml;
    boost::asio::io_context ioc; // for async operation example
    
    app a { ioc };
    assert(a.sm_.is("s1"_s));
    a.sm_.process_event(e1{});
    assert(a.sm_.is("s2"_s));
    
    ioc.run(); // async loop start until async event will become empty
}

可运行的演示:https://wandbox.org/permlink/9XFQAMHuRcMPeU64