如何使用boost::sml库从多层嵌套状态机获取当前状态?
解决Boost SML嵌套状态机当前状态名称获取问题
我们实现了三层嵌套的状态转移表Ts1(Ts2(Ts3)),使用visit_current_states方法时,仅能正确获取顶层Ts1的当前状态名称;当状态机进入Ts2或Ts3后,输出的是boost::ext::sml::v1_1_6::back::sm<...>类型字符串,而非对应状态名称。需要找到能正确获取各层级嵌套状态机当前状态的方案。
示例代码
#include <boost/sml.hpp> #include <iostream> using namespace boost::sml; struct Start {}; struct GoNested {}; struct Ts3 { auto operator()() { return make_transition_table( *"nested_s3"_s + event<Start> = "Ts3_1"_s, "nested_s3"_s + boost::sml::on_entry<_> / [] { std::puts("---- nested_s3 ----"); }, "Ts3_1"_s + boost::sml::on_entry<_> / [] { std::puts("---- Ts3_1 ----"); } ); } }; struct Ts2 { auto operator()() { return make_transition_table( *"nested_s2"_s + event<Start> = "Ts2_1"_s, "Ts2_1"_s + event<GoNested> = state<Ts3>, "nested_s2"_s + boost::sml::on_entry<_> / [] { std::puts("---- nested_s2 ----"); }, "Ts2_1"_s + boost::sml::on_entry<_> / [] { std::puts("---- Ts2_1 ----"); }); } }; struct Ts1 { auto operator()() { return make_transition_table( *"s1"_s + event<Start> = "s2"_s, "s2"_s + event<GoNested> = state<Ts2>, "s1"_s + boost::sml::on_entry<_> / [] { std::puts("---- s1 ----"); }, "s2"_s + boost::sml::on_entry<_> / [] { std::puts("---- s2 ----"); } ); } }; int main() { boost::sml::sm<Ts1, Ts2, Ts3> sMachine_; auto print = [&] { auto vis = [](auto state) { std::cout << "Current state = " << state.c_str() << std::endl; }; sMachine_.visit_current_states(vis); }; print(); sMachine_.process_event(Start{}); print(); sMachine_.process_event(GoNested{}); print(); sMachine_.process_event(Start{}); print(); sMachine_.process_event(GoNested{}); print(); sMachine_.process_event(Start{}); print(); }
原运行输出
---- s1 ---- Current state = s1 ---- s2 ---- Current state = s2 ---- nested_s2 ---- Current state = boost::ext::sml::v1_1_6::back::sm<boost::ext::sml::v1_1_6::back::sm_policy<Ts2> > ---- Ts2_1 ---- Current state = boost::ext::sml::v1_1_6::back::sm<boost::ext::sml::v1_1_6::back::sm_policy<Ts2> > ---- nested_s3 ---- Current state = boost::ext::sml::v1_1_6::back::sm<boost::ext::sml::v1_1_6::back::sm_policy<Ts2> > ---- Ts3_1 ---- Current state = boost::ext::sml::v1_1_6::back::sm<boost::ext::sml::v1_1_6::back::sm_policy<Ts2> >
解决方案
问题核心是visit_current_states默认只遍历顶层状态,遇到嵌套子状态机时需递归访问其内部状态。我们可以通过编译期类型判断,识别子状态机对象并递归调用其visit_current_states方法。
修改后的main函数代码:
int main() { boost::sml::sm<Ts1, Ts2, Ts3> sMachine_; // 递归访问状态的lambda,支持处理子状态机 auto recursive_visit = [](auto&& self, auto state) -> void { // 编译期判断是否为子状态机类型 if constexpr (requires { state.visit_current_states(self); }) { state.visit_current_states(self); } else { std::cout << "Current state = " << state.c_str() << std::endl; } }; auto print = [&] { sMachine_.visit_current_states(recursive_visit); }; print(); sMachine_.process_event(Start{}); print(); sMachine_.process_event(GoNested{}); print(); sMachine_.process_event(Start{}); print(); sMachine_.process_event(GoNested{}); print(); sMachine_.process_event(Start{}); print(); }
修改后运行输出
---- s1 ---- Current state = s1 ---- s2 ---- Current state = s2 ---- nested_s2 ---- Current state = nested_s2 ---- Ts2_1 ---- Current state = Ts2_1 ---- nested_s3 ---- Current state = nested_s3 ---- Ts3_1 ---- Current state = Ts3_1
说明
- 利用
if constexpr进行编译期类型检查,判断当前状态是否具备visit_current_states方法(即是否为子状态机) - 若为子状态机,递归调用其
visit_current_states,传入自身作为访问器 - 若为普通状态,直接输出状态名称字符串
内容的提问来源于stack exchange,提问作者adr1611
相关产品推荐
相关产品推荐

