78

ステート マシンを開発するためのインターネット上の優れたチュートリアルを誰かが知っているかどうか疑問に思っています。それとも電子ブック?

私はステートマシンの作業を始めていますが、始めるには一般的なものが必要です。

4

8 に答える 8

145

State machines are very simple in C if you use function pointers.

Basically you need 2 arrays - one for state function pointers and one for state transition rules. Every state function returns the code, you lookup state transition table by state and return code to find the next state and then just execute it.

int entry_state(void);
int foo_state(void);
int bar_state(void);
int exit_state(void);

/* array and enum below must be in sync! */
int (* state[])(void) = { entry_state, foo_state, bar_state, exit_state};
enum state_codes { entry, foo, bar, end};

enum ret_codes { ok, fail, repeat};
struct transition {
    enum state_codes src_state;
    enum ret_codes   ret_code;
    enum state_codes dst_state;
};
/* transitions from end state aren't needed */
struct transition state_transitions[] = {
    {entry, ok,     foo},
    {entry, fail,   end},
    {foo,   ok,     bar},
    {foo,   fail,   end},
    {foo,   repeat, foo},
    {bar,   ok,     end},
    {bar,   fail,   end},
    {bar,   repeat, foo}};

#define EXIT_STATE end
#define ENTRY_STATE entry

int main(int argc, char *argv[]) {
    enum state_codes cur_state = ENTRY_STATE;
    enum ret_codes rc;
    int (* state_fun)(void);

    for (;;) {
        state_fun = state[cur_state];
        rc = state_fun();
        if (EXIT_STATE == cur_state)
            break;
        cur_state = lookup_transitions(cur_state, rc);
    }

    return EXIT_SUCCESS;
}

I don't put lookup_transitions() function as it is trivial.

That's the way I do state machines for years.

于 2009-09-03T06:06:40.337 に答える
30

switch私は巨大なステートメントよりも関数ポインターを使用することを好みますが、 qrdl の回答とは対照的に、通常は明示的なリターン コードや遷移テーブルを使用しません。

また、ほとんどの場合、追加のデータを渡すメカニズムが必要になります。ステート マシンの例を次に示します。

#include <stdio.h>

struct state;
typedef void state_fn(struct state *);

struct state
{
    state_fn * next;
    int i; // data
};

state_fn foo, bar;

void foo(struct state * state)
{
    printf("%s %i\n", __func__, ++state->i);
    state->next = bar;
}

void bar(struct state * state)
{
    printf("%s %i\n", __func__, ++state->i);
    state->next = state->i < 10 ? foo : 0;
}

int main(void)
{
    struct state state = { foo, 0 };
    while(state.next) state.next(&state);
}
于 2009-09-05T14:30:27.210 に答える
10

残念ながら、ステート マシンに関する記事のほとんどは、抽象ステート クラスから派生するクラスとして FSM 実装のステートをモデル化するのが適切であるため、ポリモーフィズムを直接サポートする C++ またはその他の言語向けに書かれています。

ただし、switch ステートメントを使用してイベントを状態にディスパッチするか (単純な FSM の場​​合、ほとんどのコードが正しく記述されています)、テーブルを使用してイベントを状態遷移にマップすることを使用して、C でステート マシンを実装するのは非常に簡単です。

C でのステート マシンの基本的なフレームワークに関するシンプルで適切な記事がいくつかあります。

編集: サイト「メンテナンス中」、ウェブ アーカイブ リンク:

switchステートメントベースのステートマシンは、多くの場合、一連のマクロを使用してswitchステートメントの仕組みを「隠し」(または の代わりに // ステートメントifのセットを使用) then、C でステートマシンを記述するための「FSM 言語」に相当するものを作成します。ソース。私は個人的にテーブルベースのアプローチを好みますが、これらには確かにメリットがあり、広く使用されており、特に単純な FSM では効果的です。elseswitch

そのようなフレームワークの 1 つは、 「Game Programming Gems」の第 3.0 章 (Designing a General Robust AI Engine)で Steve Rabin によって概説されています。

同様のマクロのセットがここで議論されています:

C++ ステート マシンの実装にも興味がある場合は、他にもたくさんあります。気になる箇所があれば追記します。

于 2009-09-03T07:06:19.420 に答える
8

ステートマシンは、説明したり使用したりするためのチュートリアルを本質的に必要とするものではありません。私が提案するのは、データとその解析方法を確認することです。

たとえば、Near Space気球フライトコンピューターのデータプロトコルを解析する必要がありました。SDカードにデータを特定の形式(バイナリ)で保存し、コンマ区切りのファイルに解析する必要がありました。これにステートマシンを使用するのが最も理にかなっています。なぜなら、次の情報に応じて、解析する内容を変更する必要があるからです。

コードはC++を使用して記述されており、ParseFCUとして入手できます。ご覧のとおり、最初に解析しているバージョンを検出し、そこから2つの異なるステートマシンに入ります。

正常な状態でステートマシンに入り、その時点で解析を開始し、遭遇した文字に応じて、次の状態に進むか、前の状態に戻ります。これにより、基本的に、データの保存方法や、特定のデータが存在するかどうかにかかわらず、コードを自己適応させることができます。

私の例では、GPS文字列はフライトコンピューターがログに記録するための要件ではないため、その単一のログ書き込みの終了バイトが見つかった場合、GPS文字列の処理はスキップされる可能性があります。

ステートマシンは書くのが簡単で、一般的に私はそれが流れるべきであるという規則に従います。システムを通過する入力は、状態から状態へとある程度容易に流れる必要があります。

于 2009-09-03T05:06:36.337 に答える
5

知っておくべきことはこれだけです。

int state = 0;
while (state < 3)
{
    switch (state)
    {
        case 0:
            // Do State 0 Stuff
            if (should_go_to_next_state)
            {
                state++;
            }
            break;
        case 1:
            // Do State 1 Stuff    
            if (should_go_back) 
            {
                state--;
            }    
            else if (should_go_to_next_state) 
            {
                state++;
            }
            break;
        case 2:
            // Do State 2 Stuff    
            if (should_go_back_two) 
            {
                state -= 2;
            }    
            else if (should_go_to_next_state) 
            {
                state++;
            }
            break;
        default:
            break;
    }
}
于 2009-09-03T04:52:01.017 に答える
4

C でステート マシンを手作りするための教訓はたくさんありますが、Ragel ステート マシン コンパイラもお勧めします。

http://www.complang.org/ragel/

ステート マシンを定義する非常に簡単な方法があり、グラフを生成したり、さまざまなスタイル (テーブル駆動型、goto 駆動型) でコードを生成したり、必要に応じてそのコードを分析したりできます。また、強力であり、運用環境で使用できます。さまざまなプロトコルのコード。

于 2009-09-03T08:45:06.810 に答える
4

Real-Time Object-Oriented Modelingは素晴らしかったです (1994 年に出版され、現在はわずか 81 セント、送料 $3.99 で販売されています)。

于 2009-09-03T04:55:25.563 に答える