Search code examples
c++boost-msm

Jumping between sub SMs in boost::msm


I have two sub SMs inside my main SM. I want to be able to jump into either one from the main SM, but also jump from one sub SM to the other SM. But I can't. I'm able to jump from the main SM into the sub SMs and from one of the sub SM into the other, but when I add "mutual" transitions between the sub SM the compilation fails with ~10 errors complaining about different things. I suppose that is because the compiler goes into a recursive spin.

I suppose I can add a dummy state in the main SM with an anonymous transition into the target sub SM. But then I would loose the real event that trigged the transition and I don't want that (it contains data).

Here is some test code with the offending line commented out

#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 {

    using namespace boost::msm;
    using namespace boost::msm::front;
    namespace mpl = boost::mpl;

    struct EvGotoSub1 {};
    struct EvGotoSub2 {};

    struct MainSM_;
    using Main = back::state_machine<MainSM_>;    
    struct Sub1SM_;
    using Sub1 = back::state_machine<Sub1SM_>;
    struct Sub2SM_;
    using Sub2 = back::state_machine<Sub2SM_>;

    struct Sub1SM_ : state_machine_def<Sub1SM_> {
        struct Started : state<> { };
        using initial_state = mpl::vector<Started>;
        struct transition_table:mpl::vector<
        Row<Started, EvGotoSub2, Sub2, none, none>
        > {};
    };

    struct Sub2SM_ : state_machine_def<Sub2SM_> {
        struct Started : state<> { };
        using initial_state = mpl::vector<Started>;
        struct transition_table:mpl::vector<
        // Uncomment line below to break things
        //Row<Started, EvGotoSub1, Sub1, none, none>
        > {};
    };

    struct MainSM_ : state_machine_def<MainSM_> {
        struct Started : state<> { };
        using initial_state = mpl::vector<Started>;
        struct transition_table:mpl::vector<
        Row<Started, EvGotoSub1, Sub1, none, none>,
        Row<Started, EvGotoSub2, Sub2, none, none>
        > {};
    };
}

int main() {

    Main main;
    main.start();
    main.process_event(EvGotoSub1());
    main.process_event(EvGotoSub2());
    main.process_event(EvGotoSub1());
}

Solution

  • I assume that you don't want to nest Sub2 inside Sub1 and vice-versa, but that both are submachines to Main without any nesting. You can exit from one submachine and go to the other by using pseudo exit states.

    These exit states just forward the incoming event which you can then pass on to the other submachine by defining additional transitions in the transition table of Main:

    #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 {
    
        using namespace boost::msm;
        using namespace boost::msm::front;
        namespace mpl = boost::mpl;
    
        struct EvGotoSub1 { EvGotoSub1(int d1):d1(d1){} int d1;};
        struct EvGotoSub2 { EvGotoSub2(int d2):d2(d2){} int d2;};
    
        struct MainSM_;
        using Main = back::state_machine<MainSM_>;    
        struct Sub1SM_;
        using Sub1 = back::state_machine<Sub1SM_>;
        struct Sub2SM_;
        using Sub2 = back::state_machine<Sub2SM_>;
    
        struct Sub1SM_ : state_machine_def<Sub1SM_> {
            struct Started : state<> {  template <class Event,class Fsm> void on_entry(const Event& e, Fsm&) const { std::cout << "SUB2SM_ Started::on_entry(): d1="<<e.d1 << std::endl; } };
            struct Exit : exit_pseudo_state<EvGotoSub2> {};
            using initial_state = mpl::vector<Started>;
            struct transition_table:mpl::vector<
             Row<Started, EvGotoSub2, Exit, none, none>
            > {};
        };
    
        struct Sub2SM_ : state_machine_def<Sub2SM_> {
            struct Started : state<> {  template <class Event,class Fsm> void on_entry(const Event& e, Fsm&) const { std::cout << "SUB2SM_ Started::on_entry(): d2="<<e.d2 << std::endl; } };
            struct Exit : exit_pseudo_state<EvGotoSub1> {};
            using initial_state = mpl::vector<Started>;
            struct transition_table:mpl::vector<
            Row<Started, EvGotoSub1, Exit, none, none>
            > {};
        };
    
        struct MainSM_ : state_machine_def<MainSM_> {
            struct Started : state<> { };
            using initial_state = mpl::vector<Started>;
            struct transition_table:mpl::vector<
            Row<Started, EvGotoSub1, Sub1, none, none>,
            Row<Started, EvGotoSub2, Sub2, none, none>,
            Row<Sub2::exit_pt<Sub2SM_::Exit>, EvGotoSub1, Sub1, none, none>,
            Row<Sub1::exit_pt<Sub1SM_::Exit>, EvGotoSub2, Sub2, none, none>
            > {};
        };
    }
    
    
    int main() {
    
        Main main;
        main.start();
        main.process_event(EvGotoSub1(0));
        main.process_event(EvGotoSub2(1));
        main.process_event(EvGotoSub1(2));
    }
    

    Live example: http://coliru.stacked-crooked.com/a/d491442b38a24e82

    As you can see, the event is not lost but forwarded.

    A good ressource about the pseudo exit states can be found at http://redboltz.wikidot.com/exit-point-pseudo-state.