启动状态机无法处理启动时的内部转换

Starting state machine cannot handle internal transition on startup

我有以下状态机(抱歉,我找不到如何制作更小的 MRE):

它工作得很好,状态机按预期忽略了 Trigger 事件。 但是,如果MainSMon_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.

(强调我的)