I want a sub state-machine to be the "initial_state" of a state-machine. The following should be a broken down version of the code.
struct E {
};
struct A : public boost::msm::front::state<> {
template <class TEvent, class TStateMachine>
void on_entry(TEvent const& event, TStateMachine& stateMachine) {
}
template <class TEvent, class TStateMachine>
void on_exit(TEvent const& event, TStateMachine& stateMachine) {
}
};
struct B: public boost::msm::front::state<> {
template <class TStateMachine>
void on_entry(E const& event, TStateMachine& stateMachine) {
}
template <class TStateMachine>
void on_exit(E const& event, TStateMachine& stateMachine) {
}
};
struct SubMachineDefinition : public boost::msm::front::state_machine_def<SubMachineDefinition> {
typedef boost::msm::front::state_machine_def<SubMachineDefinition> Base;
typedef A initial_state;
struct transition_table : boost::mpl::vector<
typename Base::template _row<A, E, B>
> {};
// Added via suggestion of sehe
template <class IE, class SM> void on_entry(IE const&, SM&) { std::cout << __PRETTY_FUNCTION__ << "\n"; }
// Added via suggestion of sehe
template <class IE, class SM> void on_exit (IE const&, SM&) { std::cout << __PRETTY_FUNCTION__ << "\n"; }
};
struct SuperMachineDefinition : public boost::msm::front::state_machine_def<SuperMachineDefinition > {
typedef SubMachineDefinition initial_state;
struct transition_table : boost::mpl::vector<> {};
};
int main() {
boost::msm::back::state_machine<SuperMachineDefinition> states;
states.start();
states.process_event(E()); // This crashes
}
While this compiles fine, when I call process_event
, it crashes. Is this even possible or is there a way of debugging this?
I now set a breakpoint on A::on_entry
. When start
gets called, it does not break, so my obvious question - why is the sub state-machine not used!?
Interestingly, when I replace SubMachineDefinition
with boost::msm::back::state_machine<SubMachineDefinition>
(as initial_state
) I get a compilation error, stating it tries to call B::on_entry
with InitEvent
instead of E
. I am by now very confused.
Actually I did copy the wrong overloads for A
. I also did apply sehe's SubMachineDefinition::on_entry
and SubMachineDefinition::on_exit
definitions. Still, I get the compilation error, that it tries to call B::on_entry
with InitEvent
.
As you've already noted, you need to make the SubMachine
state a backend
typedef boost::msm::back::state_machine<SubMachineDefinition> SubMachine;
typedef SubMachine initial_state;
Next, you need to be prepared to handle (or ignore) the InitEvent
which is present so the SubMachine
can know what made it enter the initial state.
The simplest, and typical, way to do this is to just template the event argument:
struct A : public boost::msm::front::state<> {
template <class IE, class SM> void on_entry(IE const&, SM&) { std::cout << __PRETTY_FUNCTION__ << "\n"; }
template <class IE, class SM> void on_exit (IE const&, SM&) { std::cout << __PRETTY_FUNCTION__ << "\n"; }
};
But of course, you can use overloading as over to handle certain events explicitly:
struct A : public boost::msm::front::state<> {
template <class IE, class SM> void on_entry(IE const&, SM&) { std::cout << __PRETTY_FUNCTION__ << "\n"; }
template <class SM> void on_entry(E const&, SM&) { std::cout << __PRETTY_FUNCTION__ << "\n"; }
template <class IE, class SM> void on_exit (IE const&, SM&) { std::cout << __PRETTY_FUNCTION__ << "\n"; }
};
You may be tempted to write it like
template <class SM> void on_entry(typename SM::InitEvent const&, SM&) { std::cout << __PRETTY_FUNCTION__ << "\n"; }
That would indeed work, if not for the sub-state-machine. This is because the InitEvent is actually a msm::state_machine<SuperMachineDefinition>::InitEvent
, and not a msm::state_machine<SubMachineDefinition>::InitEvent
. You can do a load of template-magic to detect the InitEvent explicitly, but you should probably just use overloading as shown.
Q. It would be seriously great if someone could explain to me, how the implementation searches for the state it calls InitEvent on
The point is that it /doesn't/ search for a specific state to run that on; it uses the visitor pattern to do this, which implies that statically, all sub-states must be prepared to accept the InitEvent
.
The runtime log shows that only A::on_entry(InitEvent const&, SM&)
is invoked.
You can simply "eat" the InitEvent
. (See above, about using overloads).
In the end it comes down to separation of concerns: the sub-machine is an opaque state as seen from the super machine. The same limitation is mentioned in the documentation (#limitation-submachine) with respect to event properties:
There is, however, a limitation for submachines. If a submachine's substate has an entry action which requires a special event property (like a given method), the compiler will require all events entering this submachine to support this property.
Of course to handle InitEvent you don't need to do SFINAE wizardry, as you can just overload on the types of events that you do expect.
Q. Can you please have a look at your struct
B
, why is it not usingE
? That's the reason (cheat), why your example works
It's not a cheat. Your code was simply not satisfying the requirements on submachine state on_entry
methods.
Now that you have recognized the particular element that causes the problem in your code, it's probably wise to accept this as the path to your solution...
Fully working example Live On Coliru
#include <boost/msm/msm_grammar.hpp>
#include <boost/msm/front/state_machine_def.hpp>
#include <boost/msm/back/state_machine.hpp>
#include <iostream>
struct E {
};
struct A : public boost::msm::front::state<> {
template <class IE, class SM> void on_entry(IE const&, SM&) { std::cout << __PRETTY_FUNCTION__ << "\n"; }
template <class SM> void on_entry(E const&, SM&) { std::cout << __PRETTY_FUNCTION__ << "\n"; }
template <class IE, class SM> void on_exit (IE const&, SM&) { std::cout << __PRETTY_FUNCTION__ << "\n"; }
};
struct B : public boost::msm::front::state<> {
template <class IE, class SM> void on_entry(IE const&, SM&) { std::cout << __PRETTY_FUNCTION__ << "\n"; }
template <class IE, class SM> void on_exit (IE const&, SM&) { std::cout << __PRETTY_FUNCTION__ << "\n"; }
};
struct SubMachineDefinition : public boost::msm::front::state_machine_def<SubMachineDefinition>
{
typedef boost::msm::front::state_machine_def<SubMachineDefinition> Base;
typedef A initial_state;
struct transition_table : boost::mpl::vector<
typename Base::template _row<A, E, B>,
typename Base::template _row<B, E, A>
> {};
template <class IE, class SM> void on_entry(IE const&, SM&) { std::cout << __PRETTY_FUNCTION__ << "\n"; }
template <class IE, class SM> void on_exit (IE const&, SM&) { std::cout << __PRETTY_FUNCTION__ << "\n"; }
};
struct SuperMachineDefinition : public boost::msm::front::state_machine_def<SuperMachineDefinition > {
typedef boost::msm::back::state_machine<SubMachineDefinition> SubMachine;
typedef SubMachine initial_state;
struct transition_table : boost::mpl::vector<> {};
};
int main() {
boost::msm::back::state_machine<SuperMachineDefinition> states;
states.start();
states.process_event(E());
states.process_event(E());
states.process_event(E());
states.process_event(E());
states.process_event(E());
}