c++booststate-machineboost-msm

Deferring events from sub state machine


In my project, there is a state machine implemented using boost meta state machine. This main state machine, there is a state (let's call it SubMachineEntry for simplicity) that represents an entry point to a sub state machine:

namespace msmf = boost::msm::front;
namespace msmb = boost::msm::back;
...
msmf::Row < SomeState, enterSub, SubMachineEntry, msmf::none, msmf::none >
...
using SubMachine = msmb::state_machine<SubMachine_>;

This sub state machine performs some processing and then we go back to the outer state machine, to state

using SubMachineFinished = SubMachine::exit_pt<...>;

, which represents the exit state of the sub machine. My problem is that, before the sub state machine exits, a certain event (toProcessInOuter) might be dispatched, and it should be processed in outer state machine after sub state machine exits. Putting that event as deferred in all states in the sub state machine does not work, it is not propagated to the state that comes after SubMachineFinished. Is there a way to implement this forwarding of the event using some mechanism of the boost MSM, without using some custom workaround?


Solution

  • Unfortunately, deferred events at sub-machine cannot evaluate at the parent state-machine on MSM. So you need to write the defer transition at the parent state.

    Events are evaluated from the inner state-machine to the outer state-machine. If you want to defer the event, you can write the defer transition in the parent state. I wrote the example that demonstrate it.

    enter image description here

    See the attached diagram. Let's say Event2 is the event that you want to defer. You need to write the defer transition to State1.

    Here is the whole code:

    #include <iostream>
    #include <boost/msm/back/state_machine.hpp>
    
    #include <boost/msm/front/state_machine_def.hpp>
    #include <boost/msm/front/functor_row.hpp>
    
    namespace msm = boost::msm;
    namespace msmf = boost::msm::front;
    namespace mpl = boost::mpl;
    
    // ----- Events
    struct Event1 {};
    struct Event2 {};
    
    // ----- State machine
    struct OuterSm_:msmf::state_machine_def<OuterSm_> {
        struct State1_:msmf::state_machine_def<State1_> {
            template <class Event,class Fsm>
            void on_entry(Event const&, Fsm&) const {
                std::cout << "State1::on_entry()" << std::endl;
            }
            template <class Event,class Fsm>
            void on_exit(Event const&, Fsm&) const {
                std::cout << "State1::on_exit()" << std::endl;
            }
    
            struct SubState1:msmf::state<> {
                template <class Event,class Fsm>
                void on_entry(Event const&, Fsm&) const {
                    std::cout << "SubState1::on_entry()" << std::endl;
                }
                template <class Event,class Fsm>
                void on_exit(Event const&, Fsm&) const {
                    std::cout << "SubState1::on_exit()" << std::endl;
                }
            };
            template <class Fsm,class Event> 
            void no_transition(Event const& e, Fsm& ,int state) {
                std::cout << "No handled event in InnerSm " << typeid(e).name() << " on State " << state << std::endl;
            }
            struct Exit1:msmf::exit_pseudo_state<msmf::none> {};
            // Set initial state
            typedef mpl::vector<SubState1> initial_state;
            // Transition table
            struct transition_table:mpl::vector<
                //          Start      Event   Next        Action       Guard
                msmf::Row < SubState1, Event1, Exit1,      msmf::none,  msmf::none >
                > {};
        };
        struct State2:msmf::state<> {
            template <class Event,class Fsm>
            void on_entry(Event const&, Fsm&) const {
                std::cout << "State2::on_entry()" << std::endl;
            }
            template <class Event,class Fsm>
            void on_exit(Event const&, Fsm&) const {
                std::cout << "State2::on_exit()" << std::endl;
            }
        };
        template <class Fsm,class Event> 
        void no_transition(Event const& e, Fsm& ,int state) {
            std::cout << "No handled event in OuterSm " << typeid(e).name() << " on State " << state << std::endl;
        }
    
        typedef msm::back::state_machine<State1_> State1;
        // Actions
        struct Action {
            template <class Event, class Fsm, class SourceState, class TargetState>
            void operator()(Event const&, Fsm&, SourceState&, TargetState&) const {
                std::cout << "Action()" << std::endl;
            }
        };
        // enable deferred events
        typedef int activate_deferred_events;
        // Set initial state
        typedef State1 initial_state;
        // Transition table
        struct transition_table:mpl::vector<
            //          Start             Event       Next        Action       Guard
            msmf::Row < State1::exit_pt
                        <State1_::Exit1>, msmf::none, State2,     msmf::none,  msmf::none >,
            msmf::Row < State1,           Event2,     msmf::none, msmf::Defer, msmf::none >,
            msmf::Row < State2,           Event2,     msmf::none, Action,      msmf::none >
        > {};
    };
    
    // Pick a back-end
    typedef msm::back::state_machine<OuterSm_> Osm;
    
    
    int main() {
        Osm osm;
        osm.start(); 
    
        std::cout << "> Send Event2()" << std::endl;
        osm.process_event(Event2());
        std::cout << "> Send Event1()" << std::endl;
        osm.process_event(Event1());
        return 0;
    }
    

    Running demo: https://wandbox.org/permlink/WQixcoGGQwAWou34