【问题标题】:Boost Statechart - Local transitionsBoost Statechart - 局部转换
【发布时间】:2012-07-06 21:42:19
【问题描述】:

我希望有人可以帮助我解决这个问题,或者至少指出我的方式的错误......

作为我的问题的简单说明,请考虑应用程序的一部分,您可以在其中进入“功能模式”操作状态。然后根据用户按下的功能键 F1-F4 可以使用四种子模式。默认进入 F1 模式。状态图开始如下:

用户可以随时按F1-F4切换到相应的模式。将这些转换添加到内部状态会导致以下结果:

显然,这是 (a) 一团糟,以及 (b) 需要定义的许多转换。如果在某个时候我想添加一个 F5Mode 那么......好吧,你明白了。为避免这种情况,我想执行以下操作:

Boost Statechart 允许我定义从 FunctionMode 到任何内部状态的转换,但结果不是我所期望的。实际结果如下:

即按 F1-F4 切换模式会导致外部 FunctionMode 状态退出并重新进入,同时触发不需要的退出和进入操作。

早在 2006 年,库作者和用户之间的this thread 似乎描述了同样的问题。我认为作者建议做以下解决方法:

但是,这种变通方法对我来说似乎不是很有吸引力:它添加了一个额外的状态级别来编译,代码变得不太可读,必须使用深度历史来返回任何功能模式子状态和中间状态对象被不必要地再次破坏和构造。

那么……我哪里错了?或者有什么替代方案?我已经简要了解了 Boost Meta State Machine (msm),但从目前所见,我更喜欢 Statechart 的外观。

我很惊讶更多的用户没有遇到同样的问题......这让我觉得也许我的方法是完全错误的!

【问题讨论】:

    标签: c++ boost boost-statechart


    【解决方案1】:

    你看过statechart tutorial中解释的in-state reaction吗?它似乎正在做你正在寻找的东西。

    由于您正在寻求替代方案,因此在此期间我正在评估各种 C++ Harel 状态图实现。我查看了 Boost 状态图和 Boost MSM。我用两者都写了代码。它们伤害了我虚弱的大脑:-)

    然后我找到了Machine Objects (Macho),很简单,很小,很喜欢。它支持分层状态机、进入/退出操作、历史记录、状态机快照、守卫、内部转换、事件延迟、状态本地存储(具有可选的持久性),所以对我来说这是一个令人满意的 Harel 状态图实现。

    此代码使用 Macho 实现状态图的 FunctionMode 部分:

    #include "Macho.hpp"
    
    #include <exception>
    #include <iostream>
    using namespace std;
    
    namespace FunctionMode {
    
    struct FunctionMode;
    struct F1Mode;
    struct F2Mode;
    
    // The Top state, containing all the others.
    TOPSTATE(Top) {
        STATE(Top)
        // All the events of the state machine are just virtual functions.
    
        // Here we throw to mean that no inner state has implemented the event
        // handler and we consider that an error. This is optional, we could
        // just have an empty body or log the error.
        virtual void evF1() { throw std::exception(); }
        virtual void evF2() { throw std::exception(); }
        // evF3 and so on...
    private:
        void init() { setState<FunctionMode>(); } // initial transition
    };
    
    SUBSTATE(FunctionMode, Top) {
        STATE(FunctionMode)
        virtual void evF1() { setState<F1Mode>(); }
        virtual void evF2() { setState<F2Mode>(); }
        // evF3, ...
    private:
        void entry() { cout << "FunctionMode::entry" << endl; }
        void exit() { cout << "FunctionMode::exit" << endl; }
        void init() { setState<F1Mode>(); } // initial transition
    };
    
    SUBSTATE(F1Mode, FunctionMode) {
        STATE(F1Mode)
        virtual void evF1() {} // make the event an internal transition (by swallowing it)
    private:
        void entry() { cout << "F1Mode::entry" << endl; }
        void exit() { cout << "F1Mode::exit" << endl; }
    };
    
    SUBSTATE(F2Mode, FunctionMode) {
        STATE(F2Mode)
        virtual void evF2() {} // make the event an internal transition (by swallowing it)
    private:
        void entry() { cout << "F2Mode::entry" << endl; }
        void exit() { cout << "F2Mode::exit" << endl; }
    };
    
    } // namespace FunctionMode
    
    int main() {
    
        Macho::Machine<FunctionMode::Top> sm;
        // Now the machine is already in F1Mode.
    
        // Macho has 2 methods for synchronous event dispatching:
        // First method:
        sm->evF1(); // <= this one will be swallowed by F1Mode::evF1()
        // Second method:
        sm.dispatch(Event(&FunctionMode::Top::evF2));
    
        return 0;
    }
    

    运行它,输出是:

    FunctionMode::entry
    F1Mode::entry
    F1Mode::exit
    F2Mode::entry
    F2Mode::exit
    FunctionMode::exit
    

    这表明转换是内部的。

    在我看来,干净、简单和紧凑的代码:-)

    [EDIT1] 代码的第一个版本没有执行初始转换 FunctionMode -> F1Mode。现在可以了。

    【讨论】:

    • 非常感谢,并为延迟回复道歉,看来我没有打开电子邮件通知。不幸的是,状态反应也有同样的问题——从外部状态调用 Transit 来改变内部状态仍然会导致外部状态退出并重新进入。但是,您建议的替代方案对我来说看起来很整洁,所以谢谢!
    【解决方案2】:

    我知道这是一个老问题,这些退出->进入相同的状态很烦人。

    似乎要防止重新进入自我,您需要: 1.在“自我状态”中编写自定义处理程序 2. 在触发重新进入子状态的父处理程序中编写守卫。

    恕我直言,这是 StateChart 中的一个缺陷,我还没有找到一个好的解决方案 - 在状态机对象上调用“跳过重入状态转换”的属性会很棒。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-05-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-07
      相关资源
      最近更新 更多