boost-ext / sml

C++14 State Machine library
https://boost-ext.github.io/sml
Boost Software License 1.0
1.14k stars 177 forks source link

How to access data members in a submachine when entering & exiting and from it's substates #266

Open Eoinocal opened 5 years ago

Eoinocal commented 5 years ago

Sorry in advance for the the long code example, I felt I could better explain my goal in code rather than words. I want to store some data in a submachine and use on_entry and on_exit for initialisation and clean-up respectively, and crucially I would like to be able to access that data from substates of the submachine.

Here is a small pair of MSM statemachines which show what I'm trying to achieve

struct ev_transit {
    int i;
};
struct ev_act {};
struct ev_exit {};

struct msm_sm_sub_ : public msmf::state_machine_def<msm_sm_sub_> {

    struct nested : msm::front::state<> {};
    typedef nested initial_state;

    template <class Event, class Fsm>
    void on_entry(Event const& e, Fsm& f) {
        std::cout << "Enter sub machine, i = " << e.i <<std::endl;
        i = e.i;
    }       

    template <class Event, class Fsm>
    void on_exit(Event const& e, Fsm& f) {
        std::cout << "Exit sub machine, i = " << i <<std::endl;
        i = 0;
    }

    struct actions  {
        template <class Event, class Fsm>
        void operator()(const Event& c, Fsm& m, nested& s, nested&) {
            std::cout << "Action in nested state, i = " << m.i << std::endl;
        }
    };

    struct transition_table : mpl::vector<
        msmf::Row<nested, ev_act, nested, actions, msmf::none>
    > {};

    int i;
};

using msm_sm_sub = msm::back::state_machine<msm_sm_sub_>;

struct msm_sm_ : msmf::state_machine_def<msm_sm_> {
    struct intital : msmf::state<> {};
    typedef intital initial_state;

    struct transition_table:mpl::vector<
        msmf::Row<intital, ev_transit, msm_sm_sub, msmf::none, msmf::none >,
        msmf::Row<msm_sm_sub, ev_exit, intital, msmf::none, msmf::none >
    > {};
};

typedef msm::back::state_machine<msm_sm_> msm_sm;

Running

msm_sm foo;
foo.process_event(ev_transit{42});
foo.process_event(ev_act{});
foo.process_event(ev_exit{});

gives

Enter sub machine, i = 42
Action in nested state, i = 42
Exit sub machine, i = 42

My initial attempt at implementing this in SML is

struct sml_sub_ {
    auto operator()() noexcept {
        const auto nested = sml::state<struct nested_>;

        const auto nested_act = [this]() {
            std::cout << "Action in nested state, i = " << i << std::endl;
        };

        return sml::make_transition_table (
            *nested + sml::event<ev_act> / nested_act
        );
    }

    int i;
};

struct sml_sm {
    auto operator()() noexcept {
        const auto initial = sml::state<struct initial_>;
        const auto nested = sml::state<sml_sub_>;

        auto enter_sub = [](const ev_transit& e) { 
            std::cout << "Enter sub machine, i = " << e.i <<std::endl;
            // i = e.i; // ? Any way to access msm_sm_::i
        };

        auto exit_sub = []() { 
            std::cout << "Exit sub machine" << std::endl;
            // i = 0;
        };

        return sml::make_transition_table (
            *initial + sml::event<ev_transit> = nested,
            nested + sml::on_entry<ev_transit> / enter_sub,
            nested + sml::on_exit<sml::_> / exit_sub,
            nested + sml::event<ev_exit> = initial
        );
    }
};

As you can see I don't know how to access msm_sm_::i from the entry\exit actions.

Some workarounds I've tried:

1) The data example demonstrates a way to get the on_entry and on_ext part working, but that data is then stored in the outer state machine and inaccessible to sub states of the submachine.

Also it would be preferable to be able to store the data and the entry/exit login in the submachine for organisational reason, but this is hardly a deal breaker.

2) The *initial + sml::event<ev_transit> = nested, entry in the transition table can be augmented to repost ev_transit, then a new entry in the sml_sub_ can react to this and perform the initialisation. Works well for emulating on_entry but I can't figure out a similar trick for on_exit

Here is a file with both machine implementations sml_nested_machine_data.txt

ronen-fr commented 4 years ago

Hi. Was this issue resolved? thanks