インターネット上でステートマシンを開発するための優れたチュートリアルを知っている人はいないかと思います。それとも電子ブック?
私はステートマシンで作業を開始していますが、開始するには一般的なものが必要です。
関数ポインターを使用する場合、ステートマシンはCでは非常に単純です。
基本的に、2つの配列が必要です。1つは状態関数ポインター用で、もう1つは状態遷移規則用です。すべての状態関数がコードを返し、状態ごとに状態遷移テーブルを検索し、次の状態を見つけるためにコードを返し、それを実行します。
_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;
}
_
lookup_transition()
関数は簡単なので、入れません。
それが私が何年もステートマシンをする方法です。
巨大な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);
}
ステートマシンは、チュートリアルを説明したり使用したりすることを本質的に必要とするものではありません。私が提案するのは、データと、それをどのように解析する必要があるかを見てみることです。
たとえば、 Near Space balloon flight computer のデータプロトコルを解析する必要があり、SDカードにデータをコンマ区切りファイルに解析する必要がある特定の形式(バイナリ)で保存しました。 。ステートマシンを使用するのが最も理にかなっています。なぜなら、次の情報が何であるかに応じて、解析対象を変更する必要があるからです。
コードはC++を使用して記述されており、 ParseFC として利用できます。ご覧のとおり、まず解析しているバージョンを検出し、そこから2つの異なるステートマシンに入ります。
既知の良好な状態でステートマシンに入り、その時点で解析を開始し、遭遇する文字に応じて次の状態に進むか、前の状態に戻ります。これにより、基本的にコードはデータの保存方法と特定のデータが存在するかどうかに自己適応することができます。
この例では、GPS文字列は、フライトコンピューターがログを記録するための要件ではないため、その単一のログ書き込みの終了バイトが見つかった場合、GPS文字列の処理はスキップされます。
ステートマシンは簡単に記述でき、一般的にフローするというルールに従います。システムを通過する入力は、状態から状態へと確実に流れるはずです。
残念なことに、ステートマシンに関する記事のほとんどは、抽象状態クラスから派生するクラスとしてFSM実装の状態をモデル化するのが良いので、ポリモーフィズムを直接サポートするC++またはその他の言語向けに書かれています。
ただし、switchステートメントを使用してイベントを状態にディスパッチする(単純なFSMの場合はほとんどコードをすぐに使用する)か、テーブルを使用してイベントを状態遷移にマップすることにより、Cにステートマシンを実装するのは非常に簡単です。
Cのステートマシンの基本的なフレームワークに関する簡単ですが、まともな記事がいくつかあります。
編集:サイト「メンテナンス中」、ウェブアーカイブリンク:
switch
ステートメントベースのステートマシンは、マクロのセットを使用してswitch
ステートメントのメカニズムを「隠す」(またはif
の代わりにthen
/else
/switch
ステートメントのセットを使用)し、「FSM言語」 Cソースの状態マシンを記述します。私は個人的にテーブルベースのアプローチを好みますが、これらには確かにメリットがあり、広く使用されており、特に単純なFSMに効果的です。
このようなフレームワークの1つは、Steve Rabinが "Game Programming Gems" Chapter 3.0(一般的な堅牢なAIエンジンの設計) で概説しています。
同様のマクロのセットについては、ここで説明します。
C++ステートマシンの実装にも興味がある場合は、さらに多くのものを見つけることができます。興味があればポインタを投稿します。
Real-Time Object-Oriented Modeling は素晴らしかった(1994年に公開され、現在はわずか81セントで販売されており、送料は3.99ドルです)。
Cでハンドクラフトステートマシンを学習するための多くのレッスンがありますが、Ragelステートマシンコンパイラもお勧めします。
http://www.complang.org/ragel/
ステートマシンを定義する非常に簡単な方法があり、グラフを生成したり、さまざまなスタイル(テーブルドリブン、gotoドリブン)でコードを生成したり、必要に応じてそのコードを分析したりできます。さまざまなプロトコルのコード。
これはあなたが知る必要があるすべてです。
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;
}
}