Home / ぼやきごと / 2012-03-19
2012-03-19

C++型消去:派生クラス型を〜 の別種

Prev: [C++型消去:派生クラス型を引数に取る関数オブジェクトを基本クラスに登録して呼び出す]

前回の記事の別種です。
Character クラス側ではなく、 Controller クラス側に関数登録&実行の仕組みを実装しています。

すべて開くすべて閉じる
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
 
 
 
 
 
 
-
-
|
!
-
|
-
!
|
|
-
!
|
|
-
!
|
!
|
-
|
!
|
-
|
!
|
-
|
!
|
-
|
!
-
|
-
!
|
-
|
|
!
|
-
!
|
-
|
!
|
-
!
|
-
|
|
|
|
|
|
!
|
-
!
-
|
-
|
!
!
|
-
!
-
|
-
|
|
!
!
|
-
!
|
-
!
|
-
!
|
|
-
!
|
-
!
|
-
!
-
|
!
|
-
!
|
-
-
!
|
-
!
!
|
-
!
-
-
!
!
|
|
-
!
|
-
|
!
|
-
!
|
-
|
!
|
-
!
|
-
|
!
|
|
|
!
!
 
-
|
!
-
|
|
-
!
|
|
-
!
|
|
-
!
|
|
|
!
#include <functional>
#include <string>
#include <memory>
#include <iostream>
 
namespace
{
    ///--------------------
    /// ベースクラス
    class Character
    {
    protected:
        /// コンストラクタ。
        Character() { }
 
    public:
        /// デストラクタ。
        virtual ~Character() { }
 
    private:
        // コピー禁止
        Character(const Character&);
        void operator=(const Character&);
    };
 
    ///--------------------
    /// 派生クラス1
    class Suika : public Character { };
 
    ///--------------------
    /// 派生クラス2
    class Meiling : public Character { };
 
    ///--------------------
    /// 派生クラス3
    class Tenshi : public Character { };
 
    ///--------------------
    /// 操作クラス
    class Controller
    {
    private:
        /// メンバ関数オブジェクトを実行する。
        template<class TMemFunc, class TChara>
        void invokeCall(void* memFunc, void* chara)
        {
            TMemFunc* f = static_cast<TMemFunc*>(memFunc);
            (*f)(this, static_cast<TChara*>(chara));
        }
 
        /// メンバ関数オブジェクトを破棄する。
        template<class TMemFunc>
        void invokeDispose(void* memFunc)
        {
            delete static_cast<TMemFunc*>(memFunc);
        }
 
        /// Character クラスの派生クラス型を引数に取るメンバ関数を登録する。
        template<class TChara>
        void setFunc(void (Controller::*func)(TChara*))
        {
            resetFunc();
 
            typedef std::mem_fun1_t<void, Controller, TChara*> MemFuncType;
            _memFunc = new MemFuncType(func);
            _invokeCall = &Controller::invokeCall<MemFuncType, TChara>;
            _invokeDispose = &Controller::invokeDispose<MemFuncType>;
        }
 
        /// setFunc で登録したメンバ関数を実行する。
        void doFunc()
        {
            if (_memFunc != 0)
            {
                (this->*_invokeCall)(_memFunc, _chara.get());
            }
        }
 
        /// 登録されているメンバ関数を破棄する。
        void resetFunc()
        {
            if (_memFunc != 0)
            {
                (this->*_invokeDispose)(_memFunc);
                _memFunc = 0;
            }
        }
 
        /// メンバ関数オブジェクト
        void* _memFunc;
 
        /// メンバ関数オブジェクト実行関数
        void (Controller::*_invokeCall)(void*, void*);
 
        /// メンバ関数オブジェクト破棄関数
        void (Controller::*_invokeDispose)(void*);
 
    public:
        /// コンストラクタ。
        Controller()
            : _chara(), _memFunc(0), _invokeCall(0), _invokeDispose(0)
        {
        }
 
        /// デストラクタ。
        virtual ~Controller()
        {
            resetFunc();
        }
 
        /// 特定の派生クラスで初期化する。
        template<class TChara>
        void initialize()
        {
            // メンバ関数を登録する
            setFunc(&Controller::procCallback<TChara>);
 
            // 基本クラスにアップキャストして保持
            _chara.reset(new TChara());
        }
 
        /// 初期化した内容で処理を実行する。
        void execute()
        {
            // initialize で登録したメンバ関数を呼び出す
            doFunc();
        }
 
    private:
        /// 関数オブジェクト用の基本定義。
        template<class TChara>
        void procCallback(TChara*)
        {
            std::cout << "(Basic function)" << std::endl;
        }
 
        /// Suika 用の特殊化定義。
        template<>
        void procCallback(Suika*)
        {
            std::cout << "Ibuki Suika" << std::endl;
        }
 
        /// Meiling 用の特殊化定義。
        template<>
        void procCallback(Meiling*)
        {
            std::cout << "Chugo... Hong-Meiling!!" << std::endl;
        }
 
    private:
        std::auto_ptr<Character> _chara;
    };
}
 
///--------------------
/// メイン関数。
int main(int, char**)
{
    Controller ctrl;
 
    // Suika で初期化して実行
    ctrl.initialize<Suika>();
    ctrl.execute();
 
    // Meiling で初期化して実行
    ctrl.initialize<Meiling>();
    ctrl.execute();
 
    // Tenshi で初期化して実行
    ctrl.initialize<Tenshi>();
    ctrl.execute();
 
    return 0;
}

前回との違いは次の通り。

  • 登録する関数を Controller クラスのメンバ関数に限定している。
  • 型情報を保持する関数(invokeCall)にもメンバ関数を用いることで、 this の受け渡しを不要にしている。
  • メンバ関数 setFunc を外部から直接呼び出せないようになり、唯一呼び出しているメンバ関数 initialize<TChara> では _chara へのインスタンス設定も行っているため、追加の型チェックを行うことなく TChara 型が Character 型の派生クラスであることが保証されている。

操作対象のクラスに依存せずにコールバック的な仕組みを実装したいならこちらの方法が良いでしょう。

Category: [C++][プログラミング] - 2012-03-19 00:17:49