7

誰かがこの問題で私を助けてくれることを願っています、または少なくとも私のやり方の誤りを指摘してください...

私の問題の簡単な例として、「機能モード」の動作状態に入ることができるアプリケーションの一部を考えてみましょう。次に、ユーザーが押すファンクション キー F1 ~ F4 に応じて、4 つのサブモードを使用できます。デフォルトでは、F1 モードに入ります。状態図は次のように始まります。

図1

ユーザーはいつでも F1 ~ F4 を押して、対応するモードに切り替えることができます。これらの遷移を内部状態に追加すると、次のようになります。

ダイアグラム 2

明らかに、これは (a) 混乱しており、(b) 定義する遷移が多数あります。ある時点で F5Mode を追加したい場合は... まあ、あなたは写真を手に入れます。これを回避するために、次のことを行いたいと思います。

図 3

Boost Statechart を使用すると、FunctionMode から任意の内部状態への遷移を定義できますが、結果は期待したものではありません。実際の結果は次のとおりです。

図4

つまり、F1-F4 を押してモードを切り替えると、外側の FunctionMode 状態が終了し、不要な終了アクションと開始アクションがトリガーされて再入力されます。

2006 年にさかのぼると、ライブラリの作成者とユーザーの間のこのスレッドは、同じ問題を説明しているようです。著者は、回避策として次のことを提案していると思います。

図5

ただし、その回避策は私にはあまり魅力的ではないようです。コンパイルする余分な状態レベルが追加され、コードが読みにくくなり、関数モード サブのいずれかに戻るには深い履歴を使用する必要があります。 -状態と中間状態オブジェクトが不必要に破棄され、再構築されています。

だから...どこが間違っているのですか?または、代替手段は何ですか?Boost Meta State Machine (msm) について簡単に説明しましたが、これまで見てきたことから、Statechart の外観の方がはるかに好みです。

多くのユーザーが同じ問題に直面していないことに驚いています...おそらく私のアプローチは完全に間違っていると思います!

4

2 に答える 2

1

ステートチャートのチュートリアルで説明されている状態内反応を見ましたか? あなたが探していることをしているようです。

あなたは代替案を求めているので、この期間に私はさまざまな C++ Harel ステートチャートの実装を評価しています。BoostステートチャートとBoost MSMを見ました。私は両方でコードを書きました。彼らは私の弱い脳を傷つけます:-)

それからMachine Objects (Macho)を見つけました。これは非常にシンプルで小さく、とても気に入っています。階層的なステート マシン、エントリ/エグジット アクション、履歴、ステート マシンのスナップショット、ガード、内部遷移、イベントの遅延、状態ローカル ストレージ (オプションの永続性を使用) をサポートしているため、私にとっては満足のいく Harel ステートチャートの実装です。

このコードは、ステートチャートの FunctionMode 部分を Macho で実装します。

#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. 今はそうです。

于 2012-07-27T12:45:19.770 に答える