У меня есть следующий конечный автомат (извините, я не смог найти, как сделать MRE меньшего размера):
- SM, содержащий MainSM, содержащий SubSM.
- SM имеет внутреннюю таблицу переходов, в которой указано «игнорировать триггер события».
- При запуске начальное состояние SM — MainSM, а начальное_состояние MainSM — «Default» (поэтому это не SubSM).
- Только SubSM обрабатывает событие "Trigger".
Он работает отлично, и конечный автомат игнорирует событие Trigger
, как и предполагалось. Однако, если метод on_entry
MainSM
отправляет события 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.