基于boost::ext SML的状态机:双事件触发转换最佳实践咨询
事件驱动状态机处理多条件触发的标准方案(基于boost::ext SML)
对于这种需要同时满足多个事件/条件、且事件顺序不确定的场景,绝对不要把临时条件编码成独立状态(也就是你提到的openNotClear/openClear这类拆分方式)——这是典型的状态膨胀反模式,会大幅提升维护成本。下面是两种行业通用的标准解决方案,适配boost::ext SML的特性:
方案一:守卫条件+内部状态跟踪
核心思路是用一个内部标志跟踪持久化的条件状态(比如用户是否远离门),在触发状态转换时通过守卫条件检查所有必要条件是否满足,不用拆分状态。
这种方案能完美处理userClear在任意状态触发(包括进入open前)、甚至不触发的场景:
#include <boost/sml.hpp> struct open {}; struct close {}; struct userClear {}; struct closed_ok {}; struct door_fsm { // 内部跟踪用户是否远离,初始默认用户不在门附近 bool is_user_clear = true; auto operator()() const { using namespace boost::sml; return make_transition_table( // 初始状态:关闭 *"closed"_s + event<open> = "open"_s, // 在关闭状态时收到userClear,更新标志(不转换状态) "closed"_s + event<userClear> / [this] { is_user_clear = true; } = "closed"_s, // 开门状态下收到userClear,更新安全标志 "open"_s + event<userClear> / [this] { is_user_clear = true; } = "open"_s, // 开门状态下收到close指令,仅当安全条件满足时才转换到关闭中状态 "open"_s + event<close> [this] { return is_user_clear; } = "closing"_s, // 关闭完成后回到关闭状态,重置安全标志(可选,根据业务需求) "closing"_s + event<closed_ok> / [this] { is_user_clear = true; } = "closed"_s ); } };
优势
- 状态数量极少,逻辑清晰,避免状态爆炸
- 能长期跟踪条件状态,适配跨状态的事件触发场景
- 代码可读性高,维护成本低
方案二:利用SML的延迟事件(Deferred Events)
如果你的场景更偏向「等待某个事件来触发之前未完成的指令」,可以用SML的延迟事件特性:当close指令到来但安全条件不满足时,将该事件延迟,直到userClear触发后再自动处理延迟的close事件。
示例代码:
#include <boost/sml.hpp> struct open {}; struct close {}; struct userClear {}; struct closed_ok {}; struct door_fsm { bool is_user_clear = true; auto operator()() const { using namespace boost::sml; return make_transition_table( *"closed"_s + event<open> = "open"_s, "closed"_s + event<userClear> / [this] { is_user_clear = true; } = "closed"_s, "open"_s + event<userClear> / [this] { is_user_clear = true; } = "open"_s, // 安全条件不满足时,延迟close事件 "open"_s + event<close> [!this->is_user_clear] = defer, // 安全条件满足后,处理延迟的close事件(或直接处理新的close事件) "open"_s + event<userClear> + event<close> = "closing"_s, "open"_s + event<close> [this->is_user_clear] = "closing"_s, "closing"_s + event<closed_ok> / [this] { is_user_clear = true; } = "closed"_s ); } };
优势
- 无需手动维护等待逻辑,SML自动管理延迟事件队列
- 代码更简洁,适合一次性的事件等待场景
- 天然适配事件顺序不确定的情况
方案选择建议
- 如果条件状态需要在多个状态之间保持(比如用户是否远离的状态可能在开门、关门、关闭状态都需要跟踪),优先选方案一
- 如果只是临时等待某个事件来触发特定指令,优先选方案二
内容的提问来源于stack exchange,提问作者Nicolas
相关产品推荐
相关产品推荐

