Skip to content

[BUG] on_exit never call for initial state #188

@dfleury2

Description

@dfleury2

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,

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions