views:

411

answers:

1

Any ideas how to fix this?

using 1.39_0 on ubuntu 8.10 w/g++ 4.3.2

In the following statechart, the phrase "BUGGY" is printed three times. One would expect the event would only trigger one "BUGGY". In the case of the project I am working on, I cannot return discard_event() as I need the event to reach multiple states (usually deep in an orthogonal set of states). If there is a workaround that can be applied instead of modifying statechart, I would like to know.

$ cat bug.cpp

#include <boost/intrusive_ptr.hpp>
#include <boost/mpl/list.hpp>    #include <boost/statechart/custom_reaction.hpp>
#include <boost/statechart/event.hpp>
#include <boost/statechart/simple_state.hpp>
#include <boost/statechart/state.hpp>
#include <boost/statechart/state_machine.hpp>
#include <iostream>

using namespace std;
namespace sc = boost::statechart;
namespace mpl = boost::mpl;

struct evSay : sc::event<evSay>{ };
struct top;
struct c1;
struct c2;
struct c3;
struct sm : public sc::state_machine<sm,top> { };

struct top : sc::simple_state<top,sm,mpl::list<c1,c2,c3> > {
       typedef sc::custom_reaction<evSay> reactions;
       sc::result react(const evSay &) {
               cout<<"BUGGY"<<endl;
               return forward_event();
       }
};

struct c1 : sc::simple_state <c1, top::orthogonal<0> > { };

struct c2 : sc::simple_state <c2, top::orthogonal<1> > { };

struct c3 : sc::state <c3, top::orthogonal<2> > {
       c3( my_context  ctx) : my_base(ctx) {
               post_event( boost::intrusive_ptr< evSay > (
                               new evSay() ) );
       }
};

int main() {
       sm* fsm = new sm();
       fsm->initiate();
       delete fsm;
       return 0;
}

$ g++ bug.cpp && ./a.out
BUGGY
BUGGY
BUGGY

EDIT::

This is an example Statemachine that shows my problem that I run into in my much larger one I actually working on. I know that top will forward evSay. Note that c1,c2,c3 do not react to evSay. Here is an example where I need forwarding so that two states may react to evSay.

#include <boost/intrusive_ptr.hpp>    
#include <boost/mpl/list.hpp>    
#include <boost/statechart/custom_reaction.hpp>
#include <boost/statechart/event.hpp>
#include <boost/statechart/simple_state.hpp>
#include <boost/statechart/state.hpp>
#include <boost/statechart/state_machine.hpp>    
#include <iostream>
using namespace std;
namespace sc = boost::statechart;
namespace mpl = boost::mpl;
namespace BUG {
struct evSay : sc::event<evSay>{ };
struct top;struct c1;struct c2;struct c3;struct c2_1;

struct sm : public sc::state_machine<sm,top> { };

struct top : sc::simple_state<top,sm,mpl::list<c1,c2,c3> > {
    typedef sc::simple_state<top,sm,mpl::list<c1,c2,c3> > my_type;
    typedef sc::custom_reaction<evSay> reactions;
    sc::result react(const evSay &) {
     cout<<"BUGGY"<<endl;
     return forward_event();
    }
};

struct c1 : sc::simple_state <c1, top::orthogonal<0> > { };
struct c2 : sc::simple_state <c2, top::orthogonal<1>, c2_1 > { };
struct c3 : sc::state <c3, top::orthogonal<2> > {
    c3( my_context  ctx) : my_base(ctx) {
     post_event( boost::intrusive_ptr< evSay > (
       new evSay() ) );
    }
};

struct c2_1 : sc::simple_state<c2_1, c2 > {
    typedef sc::custom_reaction<evSay> reactions;
    sc::result react(const evSay &) {
     cout<<"CHILD REACTION"<<endl;
     return forward_event();
    }
};
}

int main()
{
    BUG::sm* fsm = new BUG::sm();
    fsm->initiate();
    delete fsm;
    return 0;
}

output: BUGGY
CHILD REACTION
BUGGY
BUGGY

A: 

One would expect the event would only trigger one "BUGGY".

I wouldn't to be honest. You specify three orthogonal states, with a react() defined on an outer state. As I understand it (*) you have three innermost states, as long as you keep forward'ing the event, all three will be processed.

Each of those three doesn't have a react, so it goes looking for a react in a outer state, and finds one in top and calls it, then as the result is a forward arbitrarily selects the next not yet visited innermost state, for which the same applies.

I cannot return discard_event() as I need the event to reach multiple states (usually deep in an orthogonal set of states).

But that's exactly what you're achieving here. You're saying you want to reach multiple states, but you don't want the react() of those states to fire? That doesn't make much sense to be honest.

It's hard to give advice as you obviously are not trying to print 'buggy' once, what exactly are you trying to achieve?

(*) for the record, I only have played with state_machine a little bit when it was new, never used it in production code, so I'm not at all an expert

Pieter
I want TOP to react to evSay. I want the ability for other states to react to evSay as well. But I do not want each child state inheriting Top's reaction to evSay. You will note my code does NOT have a reaction to evSay in c1, c2, or c3.
KitsuneYMG
So you're basically saying you're forwarding the evSay but don't really want it forwarded? You either discard, or accept that forward will actually be forwarded to the next in line...
Pieter
see edit. Why do you think forwarding an event (evSay) to a state that doesn't react to it (c1, c2, c3) should result in executing a parent's reaction? If events that aren't responded to go back up the machine, shouldn't BUGGY print 4 times? (one for top, one for each of it's 3 children that have no reactions)
KitsuneYMG