c++boosttemplate-meta-programmingboost-msm

How to implement a "BaseState" with access to back/front end of the state-machine (SM) in boost::msm


I want to share data and access between states as well as the SM as a whole and the client code (i.e. the code outside the SM).

Based on what I've come up with on the net, the best way would be to inherit all states from a base class.

Adding a base class and making all states & the SM to inherit from that is simple, but how can I add the handler to the backend/frontend of the SM as a member of this base class and how can I initialize it?

This sample code compiles, but crashes when accessing the fsmHandler set in the SubState (the SubState would not normally have access to the root fsm)!

Questions:

How can I get access to the root-SM and its data in the submachines deep down the SM-hierarchy?

Q1) How can I solve the run-time error?

Q2) How I pass data from client code (outside the SM) to the SM doesn't feel right! Is there a better way of doing this? Is it thread safe?

Q3) How can I make typedef StateBase_<MyFsm_> StateBase compile.

I would really appreciate if you could provide a working sample. Thanks for your time & help in advance.

The code:

main.cpp

int main()
{    
    std::cout << "Testing boost::msm ..." << std::endl;
    MyFsm fsm;
    fsm.start();

    MyFsm::State1& tempState = fsm.get_state<MyFsm::State1&>();    
    fsm.m_outerSMData=77;
    tempState.m_fsmHandler = &fsm;


    fsm.process_event(Event1());
    fsm.process_event(Event2());
}

myfsm.h

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

struct Event1{};
struct Event2{};

template<class Fsm>
struct StateBase_{
    //StateBase(Fsm *fsm):m_fsm(fsm){}
    StateBase_(){}
    ~StateBase_(){}

    Fsm *m_fsmHandler;
};

//typedef StateBase_<MyFsm_> StateBase;//How can I make this typedef work?

struct MyFsm_ : msmf::state_machine_def<MyFsm_, StateBase_<MyFsm_> >
{
    struct State1 : msmf::state<StateBase_<MyFsm_>>{
        template<class Event, class Fsm> void on_entry(const Event&, Fsm&) const {std::cout << "State1::on_entry()" << std::endl;}
        template<class Event, class Fsm> void on_exit(const Event&, Fsm&) const {std::cout << "State1::on_exit()" << std::endl;}
    };    

    struct State2_ : msmf::state_machine_def<State2_, StateBase_>{
        template<class Event, class Fsm> void on_entry(const Event&, Fsm&) const {std::cout << "State2::on_entry()" << std::endl;}
        template<class Event, class Fsm> void on_exit(const Event&, Fsm&) const {std::cout << "State2::on_exit()" << std::endl;}

        struct SubState21 : msmf::state<StateBase_>{
            template<class Event, class Fsm> 
            void on_entry(const Event&, Fsm&) const {
                std::cout << "SubState21::on_entry()" 
                <<"OuterSMData= "<<m_fsmHandler->m_outerSMData <<std::endl;
            }
            template<class Event, class Fsm> 
            void on_exit(const Event&, Fsm&) const {
                std::cout << "SubState21::on_exit()" << std::endl;
            }
        };
        typedef mpl::vector<SubState21> initial_state;
    };
    typedef msm::back::state_machine<State2_> State2;
   // Set initial state
   typedef State1 initial_state;

   // Transition table
   struct transition_table:mpl::vector<
         msmf::Row < State1, Event1, State2, msmf::none, msmf::none >,
         msmf::Row < State2, Event2, State1, msmf::none, msmf::none >
   >{};

  template<class Event, class Fsm>
   void no_transition(Event const&, Fsm&, int state){
       std::cout<<"no_transiton detected from state: "<< state << std::endl;
   }

   //void setPtr(int data/*MyFsm_ &fsm*/){State1::m_baseData=10;}
   int m_outerSMData=44;
};
// Pick a back-end
typedef msm::back::state_machine<MyFsm_> MyFsm;

Solution

  • You can access the outer state machine from the sub state. This is the state machine diagram based on your code.

    +-------+--------------------------------------------------------+
    | Fsm_  |  member variable: m_outerSMData                        |
    +-------+                                                        |
    |                                                                |
    |         *                                                      |
    |         |                                                      |
    |         V                                                      |
    |     +----------+      +-----------------------------------+    |
    |     |  State1  |      |      State2                       |    |
    |     +----------+Event1+-----------------------------------+    |
    |     |          |----->| on_entry/rootFsm=&f               |    |
    |     |          |      | member variable: rootFsm          |    |
    |     |          |      |  +---------------------------+    |    |
    |     |          |Event2|  | SubState21                |    |    |
    |     |          |<-----|  |                           |    |    |
    |     |          |      |  | on_entry/                 |    |    |
    |     |          |      |  | access                    |    |    |
    |     |          |      |  | f.rootFsm->m_outerSMData  |    |    |
    |     |          |      |  +---------------------------+    |    |
    |     +----------+      +-----------------------------------+    |
    +----------------------------------------------------------------+
    

    In order to access the outer state machine, State2__ need to have the pointer of MyFsm_. So I added rootFsm as the member variable of State2__ and assigns the pointer of the outer state machine to it at Stete2__::on_entry().

    If you want to access the member of the outer state machine at the Stete2__::on_entry(), you need the definition of MyFsm_. So you need to separate the member function State2__::on_entry's decralation and definition.

    Here is a key structure to achieve the goal:

    struct MyFsm_;         // forward declaration
    
    struct State2__ .... { // class definition
    
        // member function template declaration
        template<class Event, class Fsm> void on_entry(const Event&, Fsm& f);
    
        MyFsm_* rootFsm;
    };
    
    struct MyFsm_ ... {    // class definition
        // requre State2__ definition here
    };
    
    // member function template definition
    template<class Event, class Fsm> void State2__::on_entry(const Event&, Fsm& f) {
        // requre MyFsm_ definition here
        rootFsm = &f;
        std::cout << "Print OuterSMData= " << rootFsm->m_outerSMData << std::endl; 
    }
    

    Here is the live demo: https://wandbox.org/permlink/hbB405PRxc2FqG8Y