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;
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