switchを使ったステートマシンの実装と構造体配列を使ったステートマシンの実装

ステートマシンを実装するとき、一次元の状態遷移であれば、switchを使うことが多いと思いますが、構造体配列を使ったステートマシンと比較したら、どうなのか、試してみました。

まずは、switchを使ったステートマシンの実装

#include "stdio.h"

// ステート番号定義
enum
{
	INIT,
	NORMAL,
	FAIL,
	STATE_NUM
};

// 関数宣言
static void statemachine_switch(unsigned int state);

// 関数定義
static void statemachine_switch(unsigned int state)
{
	switch (state)
	{
	case INIT:
		printf("Now in INIT state. (switch)\n");
		break;
	case NORMAL:
		printf("Now in NORMAL state. (switch)\n");
		break;
	case FAIL:
		printf("Now in FAIL state. (switch)\n");
		break;
	default:
		printf("Now in default state. (switch)\n");
		break;
	}
	return;
}

int main()
{
	unsigned int i = 0;
	for (i = 0; i < STATE_NUM; i++)
	{
		// switch caseのステートマシン関数呼び出し
		statemachine_switch(i);
	}
	getchar();
    return 0;
}

引数で受け取った数値をswitchで分けて、それぞれのステートの処理をしています。

構造体配列を使ったステートマシンの実装

#include "stdio.h"

// ステートマシン用構造体の定義
typedef struct
{
	unsigned int state;	//ステート番号
	void(*func)();		//実行する関数
}_st_StateMachine;

// ステート番号定義
enum
{
	INIT,
	NORMAL,
	FAIL,
	STATE_NUM
};

// 関数宣言
static void statemachine_structarray(unsigned int state);
static void sa_INIT(void);
static void sa_NORMAL(void);
static void sa_FAIL(void);

// テーブル定義
const _st_StateMachine statearray[STATE_NUM] =
{
	{INIT, (*sa_INIT)},
	{NORMAL, (*sa_NORMAL)},
	{FAIL, (*sa_FAIL)}
};

// 関数定義
static void statemachine_structarray(unsigned int state)
{
	int i = 0;
	for (i = 0; i < STATE_NUM; i++)
	{
		if (statearray[i].state == state)
		{
			statearray[i].func();
			return;
		}
	}
	// ステート番号と一致しなかった = default
	printf("Now in default state. (struct array)\n");
	return;
}

static void sa_INIT(void)
{

	printf("Now in INIT state. (struct array)\n");
	return;
}

static void sa_NORMAL(void)
{

	printf("Now in NORMAL state. (struct array)\n");
	return;
}

static void sa_FAIL(void)
{

	printf("Now in FAIL state. (struct array)\n");
	return;
}

int main()
{
	unsigned int i = 0;
	for (i = 0; i < STATE_NUM; i++)
	{
		// 構造体配列のステートマシン関数呼び出し
		statemachine_structarray(i);
	}
	getchar();
    return 0;
}

引数で受け取った数値と一致するステート番号が構造体配列のテーブルにあるか、forループで探して、一致するものがあれば、テーブルの対応する関数ポインタを使って、関数を実行しています。

ここで注目して欲しいのは、statemachine_switch()とstatemachine_structarray()の中身です。
statemachine_switch()ではステートや、それぞれのステートでの処理が増えると、関数の複雑度が上がります。
しかし、statemachine_structarray()ではステートや、それぞれのステートでの処理が増えても関数の複雑度が上がることはありません。
構造体配列のテーブルや、それぞれのステートでの処理関数が増えるので、コードは増えますが、ステートの追加による影響が、追加したステートの関数のみで済みます。(switchの場合は、statemachine_switch()まで影響を受ける可能性がある)
コードはシンプルであることが一番いいので私は構造体配列を使ったステートマシンのほうが良いと思います。