【问题标题】:boost statechart pass arguments with transition使用转换提升状态图传递参数
【发布时间】:2011-10-21 13:58:17
【问题描述】:

我正在尝试学习 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>();
     }
};

有更好的选择吗?

【问题讨论】:

    标签: c++ boost boost-statechart statechart


    【解决方案1】:

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

    【讨论】:

    • 当我明白这一点后,我的生活变得轻松了。 +1
    • 你有可以分享的例子吗?
    【解决方案2】:

    在我在下面输入我的建议后,我在 Google 搜索中找到了this link,这表明您已经在做的事情(发布内部事件或使用数据重新发布事件)就是这样做的方法。那是来自 Boost statechart 的作者,那么谁来争论呢? :)

    我的替代建议是,如果您的任何数据在加载后存在于“项目”级别,则加载的文件名将成为 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)); 来驱动状态机;

    或者,您可以将文件名存储在“项目”状态,然后使用 context().filename 从加载状态访问它。

    【讨论】:

      猜你喜欢
      • 2015-05-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-09-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多