C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[2]: メソッドポインタ仕様の超イマイチなステートマシン


(過去ログ 131 を表示中)

[トピック内 6 記事 (1 - 6 表示)]  << 0 >>

■77415 / inTopicNo.1)  メソッドポインタ仕様の超イマイチなステートマシン
  
□投稿者/ こまお (19回)-(2015/10/19(Mon) 10:38:15)

分類:[C/C++] 

こんにちは。
Cで作った関数ポインタを使った状態遷移器がなかなか便利だったので、
そのC++版を作ろうと思い、実験コードを書いたのですが、超イマイチです。
せめてキャストをしなくて済むようにしたいです。
他にもっといい手があれば教えてください。
よろしくお願いします。

#include <iostream>
#include <vector>

class StateMachine {
public:
 typedef void (StateMachine::*state)();
 virtual ~StateMachine() {}
 void jump(state next);
 void effect();
protected:
private:
 state next_;
};

void StateMachine::jump(state next) {
 next_ = next;
}

void StateMachine::effect() {
 (this->*next_)();
}

class Test: public StateMachine {
public:
 void foo();
 void bar();
protected:
private:
};

void Test::foo() {
 std::cout << "foo!" << std::endl;
}

void Test::bar() {
 std::cout << "bar!" << std::endl;
}

int main(void)
{
 Test test;
 test.jump(static_cast<StateMachine::state>(&Test::foo)); //超イマイチ
 test.effect();
 test.jump(static_cast<StateMachine::state>(&Test::bar));
 test.effect();
}

引用返信 編集キー/
■77418 / inTopicNo.2)  Re[1]: メソッドポインタ仕様の超イマイチなステートマシン
□投稿者/ 774RR (326回)-(2015/10/19(Mon) 14:58:50)
何を持ってエレガントと呼ぶかは各個人の主観によるものだとおもうのであるが
有限要素機械なら boost/msm にあったりするのでそれを使うのもまたいとおかし

この件のイマイチさは、キャストが必要というよりは、どちらかというと
派生クラス側の情報を基底クラスで知りたい構造にあるのではないかと邪推してみる。
んで、どう修正するとよいかは、どう設計したいか次第だったりする。

とりあえず派生させなきゃいいんぢゃね?

struct test_ssm {
    typedef void (test_ssm::*action_func_type)();
    action_func_type action_func;
    void take_action() { (this->*action_func)(); }
    void set_next_action(action_func_type next_action) { action_func=next_action; }

    int first_action() { std::cout << "first_action\n"; }
    int second_action() { std::cout << "second_action\n"; }
    int final_action() { std::cout << "final_action\n"; }
};
使う側は省略

引用返信 編集キー/
■77419 / inTopicNo.3)  Re[2]: メソッドポインタ仕様の超イマイチなステートマシン
□投稿者/ こまお (20回)-(2015/10/19(Mon) 17:38:39)
ありがとうございます。

> とりあえず派生させなきゃいいんぢゃね?

そこは判ってます。つまり、派生させたいんです。
派生させて便利に使えるのを作りたいんです。
引用返信 編集キー/
■77423 / inTopicNo.4)  Re[3]: メソッドポインタ仕様の超イマイチなステートマシン
□投稿者/ 774RR (327回)-(2015/10/19(Mon) 18:38:56)
2015/10/19(Mon) 18:40:16 編集(投稿者)

typo 修正

何でも派生させるな、って習わなかった?今回の話において日本語で2文記述してみよう

A. 「 class Test はその機能の一部を有限状態機械として実装している」
B. 「 class Test は本質的に有限状態機械そのものである」

どっちがより状況を正確に表現していると思う?オイラなら A. だと思う。
ならばこの件、有限状態機械はメンバにあればよくて、派生で実装すべき内容ではない。
状況が B. なら継承で実装するのが望ましいんだけど。

実装継承って言葉もあるくらいだし(ダメな継承の典型例)
「便利な部品」は通常はメンバ変数にするものであって、継承元にするものではない。

オイラこの件(実装継承ではないにせよ)ダメな継承だと思う。

引用返信 編集キー/
■77427 / inTopicNo.5)  Re[1]: メソッドポインタ仕様の超イマイチなステートマシン
□投稿者/ yoh2 (12回)-(2015/10/19(Mon) 20:54:05)
2015/10/19(Mon) 20:58:14 編集(投稿者)
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;
    }

引用返信 編集キー/
■77431 / inTopicNo.6)  Re[2]: メソッドポインタ仕様の超イマイチなステートマシン
□投稿者/ こまお (22回)-(2015/10/20(Tue) 11:01:57)
お付き合いありがとうございます。
ここで訊くにはちょっと早かったかもです。

状態遷移の為の基本的なメカニズムをひとまとめにしておいて、後で便利に使
おうと思っているだけなので、それができるなら継承でもコンポジションでも
いいです。

実は最初は後者を思ったのですが、外側のクラスにどう『使わせるか』が難し
そうだったので、どっちにしろ最終的に出来上がるのは状態遷移器に違いない
ので、じゃぁ継承でやってみるかとやってみたら、相当イマイチな感じになっ
てきた(^^;ので、ちょっと知恵を借りてみようと書き込んでみたのでした。

コンポジションの場合は多分クラステンプレートとかになるのかなと思い手下
でスケッチしていますが、これもなかなかイマイチです(^^;

ちょっと出なおします。
解決済み
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -