如何在boost::sml状态机的direct process方法中向事件传递数据?
关于Boost SML中process动作传递状态机依赖的问题解答
问题描述
改写了Boost SML官方文档中process和defer的示例代码,尝试将外部依赖传递给状态机用于事件处理。当前疑问:能否直接在process(e2{/*此处传入my_dependency*/})中从状态机依赖列表获取数据并传递?还是只能采用lambda方式?是否支持内联实现?
附改写后的示例代码:
// $CXX -std=c++14 defer_and_process.cpp #include <boost/sml.hpp> #include <cassert> #include <deque> #include <queue> namespace sml = boost::sml; namespace { struct e1 {}; struct e2 {int dependency = 0;}; struct e3 {}; struct e4 {}; struct defer_and_process { auto operator()() const noexcept { using namespace sml; return make_transition_table( *"idle"_s + event<e1> / defer , "idle"_s + event<e2> = "s1"_s , "s1"_s + event<e1> / process(e2{/*pass my_dependency here*/}) = "s2"_s // 疑问点 , "s2"_s + event<e3> / process(e4{}) , "s2"_s + event<e4> = X ); } }; } // namespace int main() { int my_dependency = 42; using namespace sml; sm<defer_and_process, sml::defer_queue<std::deque>, sml::process_queue<std::queue>> sm{my_dependency}; assert(sm.is("idle"_s)); assert(sm.process_event(e1{})); assert(sm.is("idle"_s)); assert(!sm.process_event(e2{})); /// 触发 idle -> s1 以及 s1 -> s2(通过延迟的e1) assert(sm.is("s2"_s)); assert(sm.process_event(e3{})); /// 触发 s2.process(e4) -> X(通过处理的e4) assert(sm.is(sml::X)); }
解答
1. 直接在process(e2{...})中引用状态机依赖不可行
不能直接在process(e2{/*pass my_dependency here*/})里从状态机依赖列表获取数据,原因如下:
- 转换表
make_transition_table是在defer_and_process的operator()中编译期构建的,此时状态机实例还未创建,而my_dependency是运行时传入状态机的,两者作用域、生命周期不匹配。 operator()是const成员函数,无法访问状态机实例的依赖数据。
2. 必须使用lambda方式,且支持内联实现
正确的做法是通过lambda动作接收状态机依赖,内联完成事件构造与处理。Boost SML会自动将状态机的依赖注入到lambda参数中,只要参数类型与依赖类型匹配。
修改后的关键代码如下:
struct defer_and_process { auto operator()() const noexcept { using namespace sml; return make_transition_table( *"idle"_s + event<e1> / defer , "idle"_s + event<e2> = "s1"_s // 使用lambda接收依赖,内联构造e2并返回process动作 , "s1"_s + event<e1> / [](const int& dep) { return sml::process(e2{dep}); } = "s2"_s , "s2"_s + event<e3> / process(e4{}) , "s2"_s + event<e4> = X ); } };
3. 原理说明
当状态机构造时传入my_dependency,Boost SML会在触发对应转换时,将该依赖作为参数传递给lambda。lambda内可以直接使用这个依赖值构造e2事件,再通过sml::process将事件提交到状态机的处理队列中,实现依赖传递的效果。
内容的提问来源于stack exchange,提问作者DrDreadful
相关产品推荐
相关产品推荐

