2015/10/19(Mon) 20:57:30 編集(投稿者)
<pre><pre>2015/10/19(Mon) 20:57:20 編集(投稿者)
<pre><pre>本格的な状態機械を実装するのではなく、メンバ関数を1状態に
関連付けることで、ざくざくっと簡易的な状態機械の定義を行える
ようにしたい、ということであれば、こんなのはいかが?
元々の例では、状態遷移を Test クラスの外側で行っていますが、
状態機械を名乗るなら、状態遷移もクラス内で完結すべきだと思うので、
状態を表すメンバ関数が次の状態を返すようにしました。
StatementMachine に実装クラスを伝えるために、StatementMachine を
テンプレートにすることと、C++の型システムの枠組みで、自身と同じ型の
メンバ関数ポインタを返せるように、実質メンバ関数ポインタである
StatementMachine::Statement クラスを定義しているのがミソ。
# この手の実装に継承を利用することの可否については脇に置いての、興味本位での実装です。
#include <iostream>
// 状態機械の最基底クラス。
// StatementMachineBase *m = new 実装クラス();
// といった使い方ができるように、後述の StatementMachine の下にさらに一設けている。
class StatementMachineBase {
public:
virtual ~StatementMachineBase() {}
// 現在の状態から一歩進める。
// 終了状態に達していなければ true 、終了状態に達したら false が返る。
virtual bool step() = 0;
};
// 状態機械の基底クラスその2。
// 実装クラスはこれを派生させて作る。
template<class ImplT>
class StatementMachine : public StatementMachineBase {
public:
// 状態 ≒ メンバ関数へのポインタとみなしている。
// pfn == NULL を終了状態とみなす。
class Statement {
friend class StatementMachine;
private:
Statement (ImplT::*pfn)();
public:
Statement(Statement (ImplT::*pfn)()) : pfn(pfn) {}
};
private:
Statement current; // 現在の状態
public:
explicit StatementMachine(Statement initial) : current(initial) {}
virtual ~StatementMachine() {} // (メモ)pure virtualにするべきだが手抜き
virtual bool step() {
if(current.pfn != NULL) {
current = (static_cast<ImplT *>(this)->*current.pfn)();
}
return current.pfn != NULL;
}
};
// 実装例
class Test : public StatementMachine<Test> {
public:
Test() : StatementMachine(&Test::foo) {}
private:
// foo -> bar -> 完了となる実装
Statement foo() {
std::cout << "foo!" << std::endl;
return &Test::bar;
}
Statement bar() {
std::cout << "bar!" << std::endl;
return NULL;
}
};
int main()
{
StatementMachineBase *m = new Test();
// 終了状態に到達するまでループ。
while(m->step()) {
}
delete m;
return 0;
}