ベクトル型の要素へのアクセス
ベクトル型へ汎用的にアクセスを行うための仕組みをいくつか考えてみました。
戻り値に関しては、ひとまず、float で決め打ちしています。
(こっちもぼちぼち考えます。)
案1.全てのベクトル型に対して、関数をオーバーロードする
template<typename T, int N> T& at_x(boost::array<T, N>& v){ return v[0]; }; float& at_x(D3DXVECTOR3& v){ return v.x; }; // 以下、ベクトル型だけ定義
- メリット
- 素直で安全なコード
- 拡張しやすい
- デメリット
- 全てのベクトル型に対して、コードを書かなければならないので手間
- ユーザー定義型ならアプリケーション側で定義する必要がある
全てのベクトル型に対して、関数のオーバーロードを行ないます。
型ごとに処理を書くので、メンバ変数の有無や変数名に依存しません。
案2.float* にキャストして、アドレスを移動させる
template<typename T> float& at_x(T& v){ return *(reinterpret_cast<float*>(&v)+0); }
- メリット
- 関数が1つだけでいい
- デメリット
- 0番目のアドレスが、x要素とは限らない
- float z, y, x; と定義されている場合は…
- 要素以外のメンバ変数を持ってはいけない
- x, y, z が全て同じ型とは限らない
- 仮想関数があるだけで大惨事
ベクトル型をポインタにキャストして、そこからアドレスを移動させるやり方です。
かなり強引なやり方ですが、ベクトル型の多くは要素のみので構成されている場合が多いので、たいていの場合はこれで問題なかったりします。
以前は、このやり方でアクセスを行っていました。
デメリットがかなり目立ってますが、逆にデメリットの事をしなければ汎用的にアクセスを行うことが出来ます。
つまり、T型は、【仮想関数を持たず】、【要素数が全て同じ型】で、【アドレスの0番目が x 要素を指している】事を守れば問題ありません。
案3.ランダムアクセスイテレータを用意する
// vec 型用の begin() を作成しておく template<typename T> float& at_x(T& v){ return begin(v)[0]; }
- メリット
- 案2よりは安全にアクセスを行う事が出来る
- ランダムアクセス自体は、他の場所でも利用出来る
- ベクトルの要素以外のメンバ変数を持っていても問題ない
- デメリット
- 全てのベクトル型に対応するような、begin() 関数を作成する必要がある
- [0] が、x を指しているとは限らない
ランダムアクセスイテレータを用意して、アクセスを行います。
案2よりは安全性が高いです。
begin() と end() と iterator の辺りは、他の処理でも使う予定なのでどっちにしても必要。
案4.必要な型のみオーバーロードを行う
template<typename T> float& at_x(T& v){ return v.x; } // x を持っていない型のみオーバーロードする template<typename T, int N> T& at_x(boost::array<T, N>& v){ return v[0]; }
- メリット
- デメリット
- x 以外の名前や、boost::array の様な特殊な型はオーバーロードする必要がある
基本は、T::x でアクセスを行い、それ以外でアクセスする場合はオーバーロードを行います。
大抵の場合は、これでアクセスが可能になると思います。
もし、T::x が見つからないとコンパイルエラーが出れば、その型をオーバーロードするだけなので拡張はそんなに手間じゃないと思います。
そんな感じで、思いついたのがこの4つの方法です。
今のところ、安全性、拡張性のバランスが取れている【案4】で行こうと考えています。
他にもアグレッシブなやり方や、改善点、注意点等があれば、コメント頂ければ助かります。(いや、本当に)
他人にソースコードを見てもらえる機会がここぐらいしかないので、コメントは本当に貴重です…。