P3trus
P3trus

Reputation: 7215

boost statechart pass arguments with transition

I'm trying to learn boost::statechart.

I want to make a little app which loads a file.

//  --------------------------------
// |                                |
// |           O     Project        |
// |           |                    |
// |           v                    |
// |  ----------------------------  |
// | |                            | |
// | |         Unloaded           | |
// |  ----------------------------  |
// |  |              ^              |
// |  | EvLoad       | EvUnload     |<-----O
// |  v              |              |
// |  ----------------------------  |
// | |                            | |
// | |         Loaded             | |
// |  ----------------------------  |
// |           |   ^                |
// |           |   | EvLoad         |
// |           -----                |
//  --------------------------------

But how do i transport arguments to the state, e.g. a filename? If i store the filename inside EvLoad i can access it easily for the in state reaction

struct Loaded : sc::simple_state< Loaded, Project>
{
    typedef sc::custom_reaction< EvLoad > reactions;
    sc::result react( const EvLoad & e )
    {
        //load file e.path()
        ...
        return discard_event();
    }
}

But when I'm in the Unloaded state then I'm invoking the constructor of Loaded and i can't pass arguments to it. The only workaround I came up with is reposting the event before doing the transition, but this looks a bit dirty to me.

struct Unloaded : sc::simple_state< Unloaded, Project >
{
    typedef sc::custom_reaction< EvLoad > reactions;
     sc::result react( const EvLoad & e )
     {
         post_event( e ); //workaround to pass the event to the loaded state
         return transit<Loaded>();
     }
};

Is there a better alternative?

Upvotes: 4

Views: 3938

Answers (2)

tinman
tinman

Reputation: 6608

I found this link during a Google search after I'd typed out my suggestion below, which says that what you are already doing (posting an internal event or reposting the event with the data) is the way to do it. And that's from the author of Boost statechart, so who's to argue? :)

My alternative suggestion would be that if any of your data exists at the "Project" level once loaded then the filename that is loaded would be part of the FSM state information, at the level of "Project" rather than the "Loaded" state.

You could make the filename a parameter of the EvLoad event/constructor, performing a custom action on the transition and storing the filename that is loaded in the "Project" context. I think that is a better fit with the statechart concepts.

So something like this (although I haven't tested it), and obviously you'd clean it up to encapsulate the members better than this:

struct EvLoad: sc::event<EvLoad>
{
    std::string filename;

    EvLoad(const std::string& fn) : filename(fn) {}
};

struct EvUnload: sc::event<EvUnload>

struct Project : sc::state_machine<Project, Unloaded>
{
    std::string filename;

    void LoadFile(const EvLoad& e)
    {
        // Load file
        filename = e.filename;
    }

    void UnloadFile(const EvUnload& e)
    {
        filename.clear();
        // Unload file data
    }
};

struct Unloaded : sc::simple_state<Unloaded, Project>
{
    typedef sc::transition<EvLoad, Loaded, Project, &Project::LoadFile> reactions; 
};

struct Loaded : sc::simple_state<Loaded, Project>
{
    typdef mpl::list<
        sc::transition<EvLoad, Loaded, Project, &Project::LoadFile>,
        sc::transition<EvUnload, Unloaded>
    > reactions;
};

When you go to load a file drive the statemachine with a call such as project.process_event(EvLoad(filename));

Alternatively you could just store the filename in the "Project" state and access it with context().filename from the Loaded state.

Upvotes: 4

trs8088
trs8088

Reputation: 99

We use the triggering_event method to pull the triggering event, and then just attach the data as member variables to triggering event. It saves a lot of coding effort and keeps us from having to generate custom reactions or attaching transition variables to the statechart (two common approaches I've seen).

Upvotes: 9

Related Questions