フリーゲーム・フリーソフトの開発過程を記録していく、TDtechnic公式ブログです。製品はカテゴリの「ダウンロード場」からダウンロードして頂けます。
上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

突然ですが、皆さんはC++で無性に関数を引数で渡したいときはありませんか?C形式の関数なら関数ポインタで普通にできますが、これがメンバ関数だと途端に複雑になります。そう、メンバ関数ポインタを使う場合は何らかの形で手動でthisポインタを渡してやる必要があり、さらにそのために所属するクラスを区別しなければならないんですね。これではせっかくポインタとして扱ってもCのように汎用的には使えず、まるで役に立ちません。

と思っていた時期が私にもありました。C++版デリゲート(委譲)を知るまでは。「C++をもってC++を制す」とはこのことですね。

デリゲートは、まさに上記問題を解決するものです。メンバ関数も関数ポインタのように持ち回ったり代入したり呼んだり呼ばれたりラジバンダリできるようにしてくれます。C#は標準装備で、前作「記憶」の時はちょくちょく使った(ような気がする)んですが、私はどうせC++でそんな高級なことは出来ないだろうと思い込んでいました。

もちろん、普通のC++にデリゲートはありません。しかし、工夫次第でどうとでもなるものだったのです。鍵は「テンプレート」「継承」「仮想関数」と、C++の機能をフル活用ですね。例によってマルペケ様を主に参考にさせていただきました。

まず、呼び出すためのオブジェクトが要ります。そして、目的のメソッドへのポインタが要ります。ここでオブジェクトの型(及びメソッドの型)が不定ですね。よってテンプレートクラスDelegateObjectを作って誤魔化しました。
次に、テンプレートでコロコロ型が変わっては一つのデリゲートで保持しようがありません。よって「実行する」という仮想関数だけを持つインターフェイスIDelegateObjectを作り、それを先のテンプレートに継承させます。これで、テンプレートでどんなに化けようが、このインターフェイスへのポインタで保持していれば仮想関数から実行出来ますね。

原理はこれだけです。あとは自分が使いやすい設計になるよう細かい仕様を策定していきます。とりあえず、ポインタを包ませるためのテンプレートやらインターフェイスやらを見たくないので、専用のDelegateクラスを作ってインナークラスにしてやりました。ついでに()演算子をオーバーロードして、「オブジェクト名(引数)」とするだけで実行出来るようにしました。あたかもDelegateオブジェクトそのものが関数のようですね。
また、Delegateオブジェクトに実際にメソッドを渡す際、IDelegateObjectポインタを得るためにいちいち「Delegate::Create(this, &Foo::Bar)」などと書きたくありません。そこで、IDelegateObjectポインタを持つ新たなクラスFuncを作り、この人がコンストラクタでnewして自身のポインタに保存、そしてDelegateオブジェクトにはこのFuncオブジェクトを渡すようにしました。もちろんDelegate側のデストラクタでdeleteしますよ。これで「Func(this, &Foo::Bar)」というようにちょっと短くなりました。あ、副次効果として、引数などで型を指定するときもいちいち「Delegate::IDelegateObject* pFoo」などと書かずとも単に「Func foo」と書けるようになりました(まあ、このへんはもっと短い名前を使えば我慢出来たかもしれませんが…)。
さらに、たまにデリゲートするメンバ関数に引数を渡したいことに気づきました。そこで、DelegateクラスとFuncクラスごと更にテンプレートにし、引数の型を指定出来るようにしました。引数の数は常に一つですが、複数渡したい場合は構造体などにすればよいですね。引数がいらない場合はダミーでint型などを受け取ります(ここの使い勝手はC#の類に遠く及びませんね…もうちょっとなんとかならないかな)。可変長引数も考えましたが、型の安全性を重視してこうしました。

さあこれで、メンバ関数といふものを受け渡し出来るようになりました。これまで関数を自由定義させる方法としては仮想関数ばかり用いていましたが、これがあればいちいち継承しなくても、メインのクラスで好きに作った関数を実行させることができます。しかも自分のクラスのメンバにアクセスし放題です。派生オブジェクトをメンバに持つ方式だと、コンストラクタでいちいちメインのクラスのポインタを渡さないとメンバにアクセスすることすらままなりませんし、だからといってメインのクラス自体が継承する方式だと、同じクラスの機能を複数パターンで使うことができません。

画期的なデリゲートですが、今回製作するに当たって見たことも聞いたこともないような機能は一切使っていません。今までのC++の知識で作れるはずだったのです。やはりプログラミングというのは発想・工夫が大切なんですね。わかりやすい解説をされていた皆様に敬意を表します。
関連記事

[2015/02/17 00:54] | 不死女 -Immortal girl-
|
コメント:
この記事へのコメント:
コメント:を投稿
URL:

パスワード:
非公開コメント: 管理者にだけ表示を許可
 
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。