启动状态机无法在启动时处理内部转换
Starting state machine cannot handle internal transition on startup
我有以下状态机(抱歉,我找不到如何制作较小的 MRE(:
- SM,包含 MainSM,包含 SubSM。
- SM有一个内部转换表,上面写着"忽略事件触发器"。
- 启动时,SM 的初始状态为 MainSM,MainSM 的initial_state为"默认"(因此它不是 SubSM(。
- 只有SubSM处理"触发器"事件。
它运行良好,状态机按预期忽略Trigger
事件。但是,如果MainSM
的on_entry
方法发送Trigger
事件,则启动状态机将不会处理该事件并调用no_transition
。
问题出在哪里?调用启动时 SM 尚未准备好吗?这是一个错误还是符合规范?
这是代码片段。 删除process_event呼叫线路 80 即可正常工作。
#include <iostream>
#include <boost/core/demangle.hpp>
#include <boost/msm/back/state_machine.hpp>
#include <boost/msm/front/state_machine_def.hpp>
#include <boost/msm/front/functor_row.hpp>
#define ON_ENTRY_LOG_NAME(name)
template <class Event, class FSM>
void on_entry(const Event &, FSM&) {
std::cout << "Entering " #name << std::endl;
}
#define ON_EXIT_LOG_NAME(name)
template <class Event, class FSM>
void on_exit(const Event &, FSM&) {
std::cout << "Exitting " #name << std::endl;
}
namespace // Concrete FSM implementation
{
namespace msm = boost::msm;
namespace msmb = boost::msm::back;
namespace msmf = boost::msm::front;
namespace mpl = boost::mpl;
// events
struct Stop {};
struct Recover {};
struct Start {};
struct Trigger {};
struct SubSM_front: msmf::state_machine_def<SubSM_front>
{
struct Fidgetting: msmf::state<>
{
ON_ENTRY_LOG_NAME(Fidgetting);
ON_EXIT_LOG_NAME(Fidgetting);
};
struct FidgettingCompulsively: msmf::state<>
{
ON_ENTRY_LOG_NAME(FidgettingCompulsively);
ON_EXIT_LOG_NAME(FidgettingCompulsively);
};
using initial_state = Fidgetting;
struct transition_table: mpl::vector<
msmf::Row<Fidgetting, Trigger, FidgettingCompulsively>,
msmf::Row<FidgettingCompulsively, Trigger, Fidgetting>
> {};
ON_ENTRY_LOG_NAME(SubSM);
ON_EXIT_LOG_NAME(SubSM);
};
using SubSM = msmb::state_machine<SubSM_front>;
struct MainSM_front: msmf::state_machine_def<MainSM_front>
{
struct Default: msmf::state<>
{
ON_ENTRY_LOG_NAME(Default);
ON_EXIT_LOG_NAME(Default);
};
using initial_state = Default;
struct transition_table: mpl::vector<
msmf::Row<Default, Start, SubSM>
> {};
template <class Event, class FSM>
void on_entry(const Event &, FSM &fsm)
{
std::cout << "Entering MainSM" << std::endl;
// This line make a call to no_transition
fsm.process_event(Trigger{});
}
ON_EXIT_LOG_NAME(MainSM);
};
using MainSM = msmb::state_machine<MainSM_front>;
struct SM_front: msmf::state_machine_def<SM_front>
{
struct Stopped: msmf::state<>
{
ON_ENTRY_LOG_NAME(Stopped);
ON_EXIT_LOG_NAME(Stopped);
};
using initial_state = MainSM;
using transition_table = mpl::vector<
msmf::Row<MainSM, Stop, Stopped>,
msmf::Row<Stopped, Recover, MainSM>
>;
using internal_transition_table = mpl::vector<
msmf::Internal<Trigger>
>;
ON_ENTRY_LOG_NAME(SM);
ON_EXIT_LOG_NAME(SM);
};
using SM = msmb::state_machine<SM_front>;
void test()
{
SM sm;
sm.start();
sm.process_event(Trigger{});
sm.stop();
}
}
int main()
{
test();
return 0;
}
使用 GCC 5.5、Clang 8、Boost 1.58 和 1.73(C++14(进行测试。
在初始化SM
时,它会初始化嵌套的MainSM
。作为初始化的一部分,它将发送InitEvent
,您可以处理该以处理包含SM
实例上的TriggerEvent
。
IOW,您正在处理fsm
参数上的事件,该事件与正在初始化的SM
周围的状态机完全相同。
我认为这是不支持的。实际上,Trigger
处理"很好",但是在您的on_entry
处理程序退出后,SM
遇到了麻烦:
struct direct_event_start_helper
{
direct_event_start_helper(library_sm* self_):self(self_){}
// this variant is for the standard case, entry due to activation of the containing FSM
template <class EventType,class FsmType>
typename ::boost::disable_if<typename has_direct_entry<EventType>::type,void>::type
operator()(EventType const& evt,FsmType& fsm, ::boost::msm::back::dummy<0> = 0)
{
(static_cast<Derived*>(self))->on_entry(evt,fsm);
self->internal_start(evt);
}
该self->internal_start(evt)
不再起作用,因为外部SM纵了。断言
sotest: boost_1_72_0/boost/msm/front/state_machine_def.hpp:203: void boost::msm::front::state_machine_def<Derived, BaseState>::no_transition(const Event&, FSM&, int) [with FSM = boost::msm::back::state_machine<{anonymous}::MainSM_front>; Event = {anonymous}::Trigger; Derived = {anonymous}::MainSM_front; BaseState = boost::msm::front::default_base_state]: Assertion `false' failed.
确实意味着没有过渡:
template <class FSM,class Event>
void no_transition(Event const& ,FSM&, int )
{
BOOST_ASSERT(false);
}
文档查找
在文档中,我偶然发现了这个措辞,它似乎证实了上述所有内容:
注意:您可能已经注意到,本教程在创建后就在状态机上调用 start((。start 方法将启动状态机,这意味着它将激活初始状态,这反过来意味着将调用初始状态的进入行为。我们需要这样做的原因将在后端部分解释。调用启动后,状态机已准备好处理事件。同样,调用 stop(( 将导致调用最后一个退出操作。
(强调我的(
相关文章:
- 警告处理为错误这里有什么问题
- 在C#中处理C++指针而不使用unsafe的最佳方法
- 处理多个异常集合的C++方法
- 找不到成员对象:没有名为get_event()的成员,也处理多态性和向量
- 使用流处理接收到的数据
- 获取日期异步信号安全吗?如果在信号处理程序中使用,它会导致死锁吗
- 处理小于cpu数据总线的数据类型.(c++转换为机器代码)
- 基于多个条件处理地图中的所有元素
- 如何用数字处理log(0)
- 如何创建一个空的全局类并在启动时实例化它
- 启动状态机无法在启动时处理内部转换
- 用于处理重新启动时剩余天数的逻辑
- C#/C++:启动应用程序并处理其对系统的I/O调用
- 为什么我不能在Windows Server 2008中使用System()从服务启动批处理文件
- 批处理文件无法使用启动命令
- 正在重新启动流式处理OpenAL源
- 使用批处理重新启动 c++ 控制台应用程序不会关闭网络连接
- 关闭或重新启动后处理文件的困难
- 在未处理的异常时重新启动应用程序
- 如何启动多个线程,每个线程处理不同的文件