Boost SML:如何基于当前事件处理结果决定下一状态?
Boost SML中基于事件处理结果决定下一状态的标准做法
针对你遇到的问题,Boost SML里有两种核心方案可以解决,既避免重复计算,又能在处理完当前事件后立即判断状态转移:
1. 用上下文存储计算结果,结合守卫+内部事件实现即时判断
核心思路
- 把累加值、接收上限这类状态数据存在状态机的**上下文(context)**中,动作处理事件时只计算一次并更新上下文,后续逻辑直接读取上下文的值,彻底避免重复计算。
- 处理完
receive事件的累加动作后,发送一个内部事件(比如check_termination),在当前状态下立即处理这个内部事件,根据上下文的累加值决定是回到等待接收的状态,还是进入终止状态。
代码示例
#include <boost/sml.hpp> #include <iostream> // 定义事件 struct receive { int data; }; struct check_termination {}; // 内部判断事件 // 上下文:存储状态数据 struct receiver_ctx { int total = 0; const int upper_limit = 100; // 设定的接收上限 }; namespace sml = boost::sml; // 守卫:直接读取上下文的值,无需重复计算 auto g_not_done = [](const receiver_ctx& ctx) { return ctx.total < ctx.upper_limit; }; // 动作:处理receive事件,累加数据到上下文 auto a_receive = [](const receive& evt, receiver_ctx& ctx) { ctx.total += evt.data; std::cout << "当前累加值:" << ctx.total << std::endl; }; // 状态转移表 struct receiver_sm { auto operator()() const { using namespace sml; return make_transition_table( // idle状态收到receive事件,进入reading,执行累加后发送内部判断事件 *state<class idle> + event<receive> / (a_receive, process_event(check_termination{})) = state<class reading>, // reading状态处理内部事件,根据守卫判断转移方向 state<class reading> + event<check_termination> [g_not_done] = state<class idle>, state<class reading> + event<check_termination> = state<class terminated> ); } }; int main() { receiver_ctx ctx; sml::sm<receiver_sm> sm{ctx}; // 模拟接收事件 sm.process_event(receive{30}); sm.process_event(receive{40}); sm.process_event(receive{40}); // 累加后达到110,超过上限 return 0; }
2. 直接在动作中返回转移目标(C++17及以上)
Boost SML支持在动作中返回sml::result类型,直接指定下一状态,这种方式更简洁,无需定义额外的内部事件:
代码示例
#include <boost/sml.hpp> #include <iostream> struct receive { int data; }; struct receiver_ctx { int total = 0; const int upper_limit = 100; }; namespace sml = boost::sml; // 动作:处理累加,并根据结果返回下一状态 auto a_handle_receive = [](const receive& evt, receiver_ctx& ctx) -> sml::result { ctx.total += evt.data; std::cout << "当前累加值:" << ctx.total << std::endl; if (ctx.total < ctx.upper_limit) { return sml::transition<class idle>; // 返回转移到idle等待下一次接收 } else { return sml::transition<class terminated>; // 返回转移到终止状态 } }; struct receiver_sm { auto operator()() const { using namespace sml; return make_transition_table( *state<class idle> + event<receive> / a_handle_receive = state<class reading> // reading状态会被动作返回的转移目标覆盖,无需额外定义 ); } }; int main() { receiver_ctx ctx; sml::sm<receiver_sm> sm{ctx}; sm.process_event(receive{30}); sm.process_event(receive{40}); sm.process_event(receive{40}); return 0; }
关键说明
- 上下文的使用是避免重复计算的核心:所有需要复用的计算结果都存在上下文里,动作只做一次计算更新,守卫或后续逻辑直接读取。
- 内部事件方案兼容性更好,适合C++17以下版本;动作返回转移目标的方式更简洁,是Boost SML推荐的现代写法。
内容的提问来源于stack exchange,提问作者diwhyyyyy
相关产品推荐
相关产品推荐

