-
-
Notifications
You must be signed in to change notification settings - Fork 17
Description
Hi,
Describe the bug
The on_exit is never called for the initial state neither in the main state nor a substate. But the on_entry is sometimes call.
To Reproduce
#include <hsm/hsm.h>
#include <iostream>
#define ON_ENTRY(STATE) \
static constexpr auto on_entry() \
{ \
return [](const auto& event, const auto& source, const auto& target) { std::cout << " -- ENTRY: " << #STATE << std::endl; }; \
}
#define ON_EXIT(STATE) \
static constexpr auto on_exit() \
{ \
return [](const auto& event, const auto& source, const auto& target) { std::cout << " -- EXIT: " << #STATE << std::endl; }; \
}
#define ON(STATE) \
ON_ENTRY(STATE) \
ON_EXIT(STATE)
namespace ee {
struct Idle {
ON(Idle);
};
struct A0 {
ON(A0);
};
// --------------------------------------------------------------------------
// States
struct A1 {
ON(A1);
};
struct A2 {
ON(A2);
};
// --------------------------------------------------------------------------
// Events
struct press {};
struct start {};
struct stop {};
// --------------------------------------------------------------------------
// Guard
const auto success = [](auto /*event*/, auto /*source*/, auto /*target*/) { return true; };
// --------------------------------------------------------------------------
// Actions
const auto log = [](auto event, auto source, auto target, const char* msg = "") {
std::cout << msg << typeid(source).name() << " + " << typeid(event).name() << " = " << typeid(target).name() << std::endl;
};
// --------------------------------------------------------------------------
// State machines
struct SubState {
static constexpr auto make_transition_table()
{
// clang-format off
return hsm::transition_table(
// Source + Event [Guard] / Action = Target
// +-------------------+------------------+---------+---------+----------------------+
* hsm::state<A0> + hsm::event<press> / log = hsm::state<A1>,
hsm::state<A1> + hsm::event<press> / log = hsm::state<A2>,
hsm::state<A2> + hsm::event<press> / log = hsm::state<A0>
);
// clang-format on
}
ON(SubState);
static constexpr auto on_unexpected_event()
{
return [](auto& event, const auto& state) { log(event, state, state, "unexpected event: "); };
}
};
struct Initial {
static constexpr auto make_transition_table()
{
// clang-format off
return hsm::transition_table(
// Source + Event [Guard] / Action = Target
// +-------------------+------------------+---------+---------+----------------------+
* hsm::state<Idle> + hsm::event<start> / log = hsm::state<SubState>,
hsm::state<SubState> + hsm::event<stop> / log = hsm::state<Idle>
);
// clang-format on
}
ON(Initial);
static constexpr auto on_unexpected_event()
{
return [](auto& event, const auto& state) { log(event, state, state, "unexpected event: "); };
}
};
} // namespace ee
int main()
{
std::cout << "------------------------------------------- Initial" << std::endl;
{
hsm::sm<ee::Initial> fsm;
fsm.process_event(ee::start{});
for (int i = 0; i < 4; ++i) {
std::cout << "----- process_event(press) ----- " << std::endl;
fsm.process_event(ee::press{});
}
fsm.process_event(ee::stop{});
}
std::cout << "------------------------------------------- SubState" << std::endl;
{
hsm::sm<ee::SubState> fsm;
for (int i = 0; i < 4; ++i) {
std::cout << "----- process_event(press) ----- " << std::endl;
fsm.process_event(ee::press{});
}
}
}
Expected behavior
On_exit called every time a state is exited (at least when the on_entry was called)
Additional context
output:
------------------------------------------- Initial (with a substate)
struct ee::Idle + struct ee::start = struct ee::A0
-- ENTRY: SubState
-- ENTRY: A0
----- process_event(press) -----
here expecting EXIT: A0
struct ee::A0 + struct ee::press = struct ee::A1
-- ENTRY: A1
----- process_event(press) -----
-- EXIT: A1
struct ee::A1 + struct ee::press = struct ee::A2
-- ENTRY: A2
----- process_event(press) -----
-- EXIT: A2
struct ee::A2 + struct ee::press = struct ee::A0
-- ENTRY: A0
----- process_event(press) -----
here expecting EXIT: A0
struct ee::A0 + struct ee::press = struct ee::A1
-- ENTRY: A1
-- EXIT: SubState
<- here the sate on_exit is called which is great
struct ee::SubState + struct ee::stop = struct ee::Idle
-- ENTRY: Idle
<- here ENTRY is called but was not called at the start, nor the EXIT is called
------------------------------------------- SubState
----- process_event(press) -----
here, may be EXIT expected, may by ENTRY too for A0
struct ee::A0 + struct ee::press = struct ee::A1
-- ENTRY: A1
----- process_event(press) -----
-- EXIT: A1
struct ee::A1 + struct ee::press = struct ee::A2
-- ENTRY: A2
----- process_event(press) -----
-- EXIT: A2
struct ee::A2 + struct ee::press = struct ee::A0
-- ENTRY: A0
----- process_event(press) -----
here ENTRY: A0 was called, but not EXIT
struct ee::A0 + struct ee::press = struct ee::A1
-- ENTRY: A1
Unfortunately, the code itself is beeyond my template/boost.Hana knowledge, If I have some tips to find the issue or change the behavior, may be I can help.
Regards,