March 09, 2009

C++でリフレクションもどき

物理法則に関係するプログラミングをしていると、変数に人間にわかる名前をつけておかないと大変な事態になります。特にその変数が配列であるときは悲劇で、インデックスが直打ちされている残念なコードを時たま見かけます。このようなコードはインデックスが少し違っただけで計算結果が発散してしまいデバックできなくなることは必至です。
少々経験を積むと、例えばCやC++言語ならdefineマクロやenum(列挙体)等を利用することによって、可読性を高めミスを防止できます。コンパイル時に名前とインデックスの対応関係がプリプロセッサまたはコンパイラによって解決されるためです。
しかし実行時にも似たような事態が発生します。例えば外部から値を取り込んで指定の変数に設定するプログラムがあるとしましょう。文字列として取り込まれた名前とインデックスの対応関係は、defineやenum自体でつけることができないので、ハッシュテーブルなどを使った変換表を別に用意する必要があります。あるいは言語によっては、リフレクションの機能を使うことによって、この対応関係を動的に求めることが可能です。

前置きが長くなりましたが、今回はリフレクションの機能を持たないC++言語で、実行時にも名前からインデックスを引けるような簡単な記述方法を模索してみることにしました。解はいくつかあると思いますが、結果は以下のようになりました。

とりあえず使う分には49行目までを別のヘッダにでも定義しておき、本体には51行目~55行目の4行を書くだけで、1) コードに書いた名前で配列にアクセス、 2) 実行時に外部から取り込んだ名前から配列のインデックスを求め配列にアクセス の両方が実現されています。1) の機能はMAKE_ALIASマクロ内で展開される関数定義によっています。2) の方は少々複雑で、テンプレートの特殊化によって対応しています。内部的にリンクリストを作成することで表引きを可能にしました。

注目なのは、リンクリストを実現するためのテンプレートの特殊化方法です。テンプレートの特殊化を行うのは、通常グローバルスコープ(簡易的に言えばmain()関数と同じレベル)なので、あるクラスの中でテンプレートの特殊化をすることはあまり機会がないと思います。しかしながらそのような方針をとると、MAKE_ALIASマクロを2つのマクロ、すなわち、1)の機能を実現する関数(クラス内にある必要がある)を定義するマクロと 2)の機能を実現するためのテンプレート特殊化マクロ(方針に従うのならクラスの外)に分けなければならなくなります。重要なことなので同じ名前に関して2回定義してもいいのですが(笑)、重複は悪なので1つのマクロにまとめたいという結果が今回のコードです。こうしておくとさらにいいことがあって、クラスの中でこのMAKE_TABLEやMAKE_ALIASマクロを使っても変数定義が同じようにできます。

テクニックとしては、クラスの中でテンプレート特殊化を行うためダミー型(コードではTで表記)を利用した部分特殊化を使っています。実は部分特殊化を使わず、完全特殊化であってもM$系のコンパイラなら通ってしまうのですが、それは独自拡張だそうなので仕様に準拠して部分特殊化としました。『c++スレより「クラスの内側で定義したクラステンプレートを特殊化する」』がとても参考になりました。

なお今回コードを紹介するにあたって利用したCodepadはweb上でコードが実行できる優れものです。とても有用なwebサービスだと思います。

12:27 fenrir が投稿 : 固定リンク | | このエントリーを含むはてなブックマーク | トラックバック
このエントリーのトラックバックURL: https://fenrir.naruoka.org/mt/mt-tb.cgi/691
コメント
コメントする









名前、アドレスを登録しますか?
(次回以降コメント入力が楽になります)
  • 匿名でのコメントは受け付けておりません。
  • 名前(ハンドル名可)とメールアドレスは必ず入力してください。
  • メールアドレスを表示されたくないときはURLも必ず記入してください。
  • コメント欄でHTMLタグは使用できません。
  • コメント本文に日本語(全角文字)がある程度多く含まれている必要があります。
  • コメント欄内のURLと思われる文字列は自動的にリンクに変換されます。