Boost.MSM无法正确处理多态事件的问题该如何解决?
问题根因
Boost.MSM的事件派发默认是编译期静态绑定实现的,它会直接根据传入process_event的参数的静态类型匹配状态机转换表中的对应规则,不会利用C++运行时多态识别指针指向的实际派生类类型。你遍历容器时解引用shared_ptr<EvtBase>得到的参数静态类型是EvtBase,因此状态机只会按照基类类型做事件匹配,无法识别到实际的EvtA/EvtB类型,这个问题不属于使用错误,是Boost.MSM为了追求运行时性能的默认设计。
可行解决方案
方案1:通过访问者模式实现动态派发
在事件基类中定义虚函数实现动态分发,是兼容C++任意版本、侵入性较低的通用解决方案:
- 改造事件基类与派生类,新增虚回调函数:
// 前置声明状态机类型,避免头文件循环依赖 class MyFsm; struct EvtBase { virtual ~EvtBase() = default; virtual void dispatch(MyFsm& fsm) const = 0; }; struct EvtA : EvtBase { void dispatch(MyFsm& fsm) const override; }; struct EvtB : EvtBase { void dispatch(MyFsm& fsm) const override; };
- 在状态机定义完成后,实现派生类的dispatch逻辑,直接投递实际类型的事件:
// 此处需保证MyFsm类型已经完整定义 void EvtA::dispatch(MyFsm& fsm) const { fsm.process_event(*this); } void EvtB::dispatch(MyFsm& fsm) const { fsm.process_event(*this); }
- 投递事件时直接调用虚函数即可:
for (const auto& pEvt: event_list) { pEvt->dispatch(fsm); }
方案2:用std::variant存储事件(C++17及以上)
如果所有事件类型在编译期完全可知,推荐用变体类型替代基类指针存储,无需修改事件结构,没有虚函数开销,性能更高:
// 定义所有可能的事件类型的变体 using EventVariant = std::variant<EvtA, EvtB>; std::vector<EventVariant> event_list {EvtA{}, EvtB{}}; // 遍历投递时用visit匹配实际类型 for (auto& evt : event_list) { std::visit([&](auto&& real_evt) { fsm.process_event(real_evt); }, evt); }
如果使用C++17以下的标准,可以用Boost.Variant实现相同逻辑。
内容的提问来源于stack exchange,提问作者auzn
相关产品推荐
相关产品推荐

