启动状态机无法处理启动时的内部转换
Starting state machine cannot handle internal transition on startup
我有以下状态机(抱歉,我找不到如何制作更小的 MRE):
- SM,包含MainSM,包含SubSM。
- SM 有一个内部转换 table 表示 "ignore event Trigger"。
- 启动时SM的初始状态为MainSM,MainSM的initial_state为"Default"(所以不是SubSM)
- 只有 SubSM 处理 "Trigger" 事件。
它工作得很好,状态机按预期忽略了 Trigger
事件。 但是,如果MainSM
的on_entry
方法发送了Trigger
事件那么启动状态机将不会处理事件并调用no_transition
.
问题是什么?是不是调用start的时候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
已被处理 "just fine",但在您的 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);
}
文档查找
在文档中我偶然发现了这个似乎证实了以上所有内容的措辞:
Note: you might have noticed that the tutorial calls start() on the state machine just after creation. The start method will initiate the state machine, meaning it will activate the initial state, which means in turn that the initial state's entry behavior will be called. The reason why we need this will be explained in the back-end part. After a call to start, the state machine is ready to process events. The same way, calling stop() will cause the last exit actions to be called.
(强调我的)
我有以下状态机(抱歉,我找不到如何制作更小的 MRE):
- SM,包含MainSM,包含SubSM。
- SM 有一个内部转换 table 表示 "ignore event Trigger"。
- 启动时SM的初始状态为MainSM,MainSM的initial_state为"Default"(所以不是SubSM)
- 只有 SubSM 处理 "Trigger" 事件。
它工作得很好,状态机按预期忽略了 Trigger
事件。 但是,如果MainSM
的on_entry
方法发送了Trigger
事件那么启动状态机将不会处理事件并调用no_transition
.
问题是什么?是不是调用start的时候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
已被处理 "just fine",但在您的 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);
}
文档查找
在文档中我偶然发现了这个似乎证实了以上所有内容的措辞:
Note: you might have noticed that the tutorial calls start() on the state machine just after creation. The start method will initiate the state machine, meaning it will activate the initial state, which means in turn that the initial state's entry behavior will be called. The reason why we need this will be explained in the back-end part. After a call to start, the state machine is ready to process events. The same way, calling stop() will cause the last exit actions to be called.
(强调我的)