通过转换来提升状态图传递参数

时间:2011-08-06 18:40:55

标签: c++ boost boost-statechart statechart

我正在尝试学习boost :: statechart。

我想创建一个加载文件的小应用程序。

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

但是我如何将参数传递给州,例如文件名? 如果我将文件名存储在EvLoad中,我可以轻松访问状态反应

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();
    }
}

但是当我处于Unloaded状态时,我正在调用Loaded的构造函数,我无法将参数传递给它。我提出的唯一解决方法是在转换之前重新发布事件,但这看起来有点脏。

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>();
     }
};

有更好的选择吗?

2 个答案:

答案 0 :(得分:9)

我们使用triggering_event方法来拉取触发事件,然后将数据作为成员变量附加到触发事件。它节省了大量的编码工作,使我们不必生成自定义反应或将转换变量附加到状态图(我见过的两种常见方法)。

答案 1 :(得分:4)

我在下面输入我的建议后,在Google搜索过程中找到了this link,其中说明您正在做的事情(发布内部事件或将事件与数据一起重新发布)是可行的方法它。那是来自Boost状态图的作者,那么谁来争论呢? :)

我的替代建议是,如果您的任何数据一旦加载就存在于“项目”级别,那么加载的文件名将成为FSM状态信息的一部分,在“项目”级别而不是“已加载” “州。

您可以将文件名设置为EvLoad事件/构造函数的参数,对转换执行自定义操作并存储在“项目”上下文中加载的文件名。我认为这更适合状态图概念。

所以这样的事情(虽然我还没有测试过),显然你要把它清理干净,比这更好地封装成员:

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

当您使用诸如project.process_event(EvLoad(filename))之类的调用来加载文件驱动器状态机时;

或者,您可以将文件名存储在“Project”状态,并使用来自Loaded状态的context()。filename访问它。