I'm using boost.msm from the boost 1.52 library and trying to fire an event
from a composite state to enter a state of a top level state machine.
Unfortunatly this doesn't work.
Please consider here for the following code:
namespace {
namespace msm = boost::msm;
namespace msmf = boost::msm::front;
namespace mpl = boost::mpl;
// ----- Events
struct Event1 {};
struct Event2 {};
struct EventSub1 {};
struct EventSub2 {};
// ----- 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& fsm) const {
std::cout << "SubState1::on_entry()" << std::endl;
std::cout << "SubState1::fire event2()" << std::endl;
fsm.process_event(Event2()); // Produces an assertion ->
It is not possible to leave the submachine although the "Event2()" is
defined as transition in the transition table of the outermachine.
}
template <class Event,class Fsm>
void on_exit(Event const&, Fsm&) const {
std::cout << "SubState1::on_exit()" << std::endl;
}
};
struct SubState2:msmf::state<> {
template <class Event,class Fsm>
void on_entry(Event const&, Fsm& a) const {
std::cout << "SubState2::on_entry()" << std::endl;
}
template <class Event,class Fsm>
void on_exit(Event const&, Fsm&) const {
std::cout << "SubState2::on_exit()" << 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, EventSub1, SubState2, msmf::none,
msmf::none >
// Is there a possibility in MSM to exit a composite state if
an event is fired from that
// msmf::Row < SubState1, Event2, Exit1, msmf::none,
msmf::none > I don't want this!
> {};
};
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 Event, class Fsm, class SourceState, class
TargetState>
void operator()(Event const&, Fsm& f, SourceState&,
TargetState&) const
{
std::cout << "Do function" << std::endl;
}
};
typedef msm::back::state_machine<State1_> State1;
// Set initial state
typedef State1 initial_state;
// Transition table
struct transition_table:mpl::vector<
// Start Event Next Action Guard
msmf::Row < State1, Event2, State2, State2, msmf::none
>
> {};
};
// Pick a back-end
typedef msm::back::state_machine<OuterSm_> Osm;
void test()
{
Osm osm;
osm.start();
// osm.process_event(Event2()); // This works fine -> Leaving state
"Substate1" of the submachine
}
}
int main()
{
test();
return 0;
}
You see that the code calls f*sm.process_event(Event2())* within the state
*SubState1* of the submachine to make a transition from the top-level state
*State1* to *State2* of the outer statemachine. Unfortunately this doesn't
work although the *Event2* is defined in the transition table of the
outer-statemachine.
On the other hand it a transition from *State1* to state *State2* works in
case the event *Event2* is fired outside the submachine ( please refer to
the void test() function in the code above).
I actually don't want to define a transition in the transition table of
submachine to enter an pseudo exit point like:
msmf::Row < SubState1, Event2, Exit1, msmf::none, msmf::none >
Is there a possibility in MSM to exit a composite state if an event is fired
from that composite state?
Thank you in advance for your time and help!
Grüße ;),
RaRi
--
View this message in context: http://boost.2283326.n4.nabble.com/Boost-MSM-Exit-a-composite-state-after-an-event-has-been-fired-from-that-composite-state-tp4640341.html
Sent from the Boost - Users mailing list archive at Nabble.com.
_______________________________________________
Boost-users mailing list
Boost...@lists.boost.org
http://lists.boost.org/mailman/listinfo.cgi/boost-users
<snip code>
> You see that the code calls f*sm.process_event(Event2())* within the state
> *SubState1* of the submachine to make a transition from the top-level
> state
> *State1* to *State2* of the outer statemachine. Unfortunately this doesn't
> work although the *Event2* is defined in the transition table of the
> outer-statemachine.
>
> On the other hand it a transition from *State1* to state *State2* works
> in
> case the event *Event2* is fired outside the submachine ( please refer to
> the void test() function in the code above).
>
> I actually don't want to define a transition in the transition table of
> submachine to enter an pseudo exit point like:
>
> msmf::Row < SubState1, Event2, Exit1, msmf::none, msmf::none >
>
> Is there a possibility in MSM to exit a composite state if an event is
> fired
> from that composite state?
It's a question which is asked regularly on different forms. It usually
boils down to a current limitation, that the FSM template parameter refers
to the lowest level fsm, not the outer, and you want to send the event to
the outest to get correct handling.
Actually I got this asked a few days ago. You need to get to the lower-level
fsm a way to call the outest:
- let the outer state machine set a pointer to itself into each submachine
- hide this inside a boost::function. Unfortunately it means a
boost::function for every event
- use type_erasure (not in boost yet) to have just one object with several
members instead of many boost::function objects
I'm aware of this limitation and I want to provide a solution (a variadic
list of FSMs) right after the thread thing you just asked. In the meantime
you will have to use one of the above solutions.
> Thank you in advance for your time and help!
>
> Grüße ;),
:)
Grüße,
Christophe
Thanks for your fast response again.
I was thinking so solve my problem in another way, but I was again
disappointed.
My idea was to manage a global queue/vector for different event types. The
"global" queue could be used by the submachine to put internal events in
which than would be afterwards extracted from the State machine caller and
emited to the state machine by using the fsm.process_event(...) function.
but unfortunately this seems not to work too. Please consider the following
code snippet to understand me:
#include <iostream>
#include <boost/msm/back/state_machine.hpp>
#include <boost/msm/front/state_machine_def.hpp>
#include <boost/msm/front/functor_row.hpp>
#include <boost/variant/variant.hpp>
namespace {
namespace msm = boost::msm;
namespace msmf = boost::msm::front;
namespace mpl = boost::mpl;
// ----- Events
struct Event1 {};
struct Event2 {};
struct Event3 {};
struct EventSub1 {};
struct EventSub2 {};
// ----- 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& 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;
}
};
struct SubState2:msmf::state<> {
template <class Event,class Fsm>
void on_entry(Event const&, Fsm& a) const {
std::cout << "SubState2::on_entry()" << std::endl;
}
template <class Event,class Fsm>
void on_exit(Event const&, Fsm&) const {
std::cout << "SubState2::on_exit()" << 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, EventSub1, SubState2, 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 Event, class Fsm, class SourceState, class
TargetState>
void operator()(Event const&, Fsm& f, SourceState&,
TargetState&) const
{
std::cout << "Do function" << std::endl;
}
};
struct State3:msmf::state<>
{
template <class Event,class Fsm>
void on_entry(Event const&, Fsm&) const {
std::cout << "State3::on_entry()" << std::endl;
}
template <class Event,class Fsm>
void on_exit(Event const&, Fsm&) const {
std::cout << "State3::on_exit()" << std::endl;
}
};
typedef msm::back::state_machine<State1_> State1;
// Set initial state
typedef State2 initial_state;
// Transition table
struct transition_table:mpl::vector<
// Start Event Next Action Guard
msmf::Row < State2, Event2, State2, msmf::none, msmf::none
>,
msmf::Row < State3, Event3, msmf::none, msmf::none,
msmf::none >
> {};
};
// Pick a back-end
typedef msm::back::state_machine<OuterSm_> Osm;
void test()
{
Osm osm;
osm.start();
std::vector< boost::variant<Event1, Event2, Event3> > vec;
// With boost::any it also doesn't work
// std::vector< boost::any > vec;
vec.push_back(Event1());
vec.push_back(Event2());
vec.push_back(Event3());
while(vec.size() != 0){
osm.process_event(vec.pop_back());
}
}
}
int main()
{
test();
return 0;
}
Compiling is fine, but while running this example I get again a
BOOST_ASSERTION, cause the event type isn't known.
I think the base problem occurs in the compilation phase, due to the fact,
that the template event type of the *process_event(...)* function isn't
distinct due to the different types defined in *boost::variant<...>* as
state above in the main() function. In contrast to that the following code
snippet works fine.
....
osm.enqueue_event(Event1());
osm.enqueue_event(Event2());
osm.enqueue_event(Event3());
osm.execute_queued_events();
...
Using the *enqueue_event()* function call has unfortunately the constraint,
that for each sub-machine a own event queue is used and for each sub-machine
the call execute_queued_events has to be triggered. I don't want this. I
want to queue all possible events of the all submachines and to call
execute_queued_events function from a central position.
Do you have an idea how I could solve this issue. Does MSM provide a global
event queue? If not, it would be necessary to provide each sub-machine with
a pointer to the outermost submachine to be able to put events to the event
queue. Afterwards "execute_queued_events()" can be called by the
control-logic which encloses the overal state machine.
Thank you in advance!
Sorry für die vielen Fragen, muss jedoch abwägen, ob MSM für den produktiven
Einsatz in
einem noch zu entwickelden Software-Produkt geeignet ist.
Grüße,
RaRi
--
View this message in context: http://boost.2283326.n4.nabble.com/Boost-MSM-Exit-a-composite-state-after-an-event-has-been-fired-from-that-composite-state-tp4640341p4640414.html
Sent from the Boost - Users mailing list archive at Nabble.com.
> I think the base problem occurs in the compilation phase, due to the fact,
> that the template event type of the *process_event(...)* function isn't
> distinct due to the different types defined in *boost::variant<...>* as
> state above in the main() function.
Indeed, MSM does not support boost::variant. It knows only concrete event
types.
>In contrast to that the following code
> snippet works fine.
>
> ....
> osm.enqueue_event(Event1());
> osm.enqueue_event(Event2());
> osm.enqueue_event(Event3());
>
> osm.execute_queued_events();
>
> ...
>
> Using the *enqueue_event()* function call has unfortunately the
> constraint,
> that for each sub-machine a own event queue is used and for each
> sub-machine
> the call execute_queued_events has to be triggered. I don't want this. I
> want to queue all possible events of the all submachines and to call
> execute_queued_events function from a central position.
Same as last question, you need to enqueue on the top-level fsm, then it
should work. This is no different from what we just discussed.
I know it's not fun to keep a pointer to the top-level fsm but for the time
being, there is no choice. I don't think trying to work around this will
work (I tried ;-) ).
> Do you have an idea how I could solve this issue. Does MSM provide a
> global
> event queue? If not, it would be necessary to provide each sub-machine
> with
> a pointer to the outermost submachine to be able to put events to the
> event
> queue. Afterwards "execute_queued_events()" can be called by the
> control-logic which encloses the overal state machine.
Each fsm has its own event queue, including submachines. If you enqueue into
the one of the top-level fsm, it will work fine.
Actually, this feature is pretty basic. If you look at the code, it just
builds a list of boost::function of functors created by boost::bind.
You could easily build your own if this one does not fit your needs, there
really is no magic there.
> Thank you in advance!
>
> Sorry für die vielen Fragen, muss jedoch abwägen, ob MSM für den
> produktiven
> Einsatz in
> einem noch zu entwickelden Software-Produkt geeignet ist.
Sorry that I answer fast, I'm leaving for 2 weeks tomorrow and will be only
online again on the 7th Jan.
> Grüße,
>
> RaRi
Cheers,
Christophe