式鋳型
書いては消しを繰り返しているもんだから何も進んでいない。なんかおもろいもんは無かろうか?循環参照も殺せるスマートポインタとか面白そうだが、参照を追跡しようと思ったらクラスのメンバーにあるスマートポインタを追跡する必要があって、こればっかりは自動化できない。これをどれだけナイスな方法でもって解決するかというところか。
で、それはそれとして最近書いたコードと言えば会社で書いてた3次元用vector, matrix templateだが、Expression Templateで実装してみた。本当はuBLASを使いたかったのだが、なんというかやってみたくなったので自作した次第。以下は自分とあと誰かの参考になればいいかも知れないメモ。
とりあえず3次元のベクトルを表現するクラスのインターフェースを決める
- operator[]で要素取得
- operator=で右辺のベクトルを丸ごと左辺に格納
- operator+, operetor-で足したり引いたり
- dot_prod()でドット積
- cross_prod()でクロス積
とりあえずはこれぐらいで良かろうか?あと計算するベクトルの要素型が違ってても(vector
で、私が書いた実装はこんな感じ(正確ではない)。このソースコードでは適切なパラメータ渡しとかはやってない。
// mixin template template<typename Vec, typename Elem> struct vector_base { typedef Vec real_type; typedef Elem element_type; protected: template<typename V> void assign(V const & v) { real_type& self = static_cast<real_type&>(*this); for (unsigned char i = 0; i < 3; i++) { self[i] = v[i]; } } // その他共通実装など }; // 内部に配列を保持するvector template<typename Elem> struct vector : public vector_base<vector<Elem>, Elem> { typedef Elem element_type; private: element_type array_[4]; public: element_type & operator[](std::size_t i) { return array_[i]; } element_type const & operator[](std::size_t i) const { return array_[i]; } template<typename Vec> vector& operator=(Vec const& v) { assign(v); return *this; } template<typename Vec> vector(Vec const & v) { assign(v); } vector(Elem const & x, Elem const & y, Elem const & z) { array_[0] = x; array_[1] = y; array_[2] = z; array_[3] = element_type(1); } }; // ベクトル同士の二項演算子ベース template<typename Expr, typename Lhs, typename Rhs> struct vector_binary_op : public vector_base<Expr, typename promote_type<typename Lhs::element_type, typename Rhs::element_type>::type> { typedef typename promote_type<typename Lhs::element_type, typename Rhs::element_type>::type element_type; protected: Lhs const & lhs_; Rhs const & rhs_; vector_binary_op(Lhs const & lhs, Rhs const & rhs) : lhs_(lhs), rhs_(rhs) {} }; // ベクトル加算のクラス template<typename Lhs, typename Rhs> struct vector_add : public vector_binary_op<vector_add<Lhs, Rhs>, Lhs, Rhs> { typedef vector_binary_op<vector_add<Lhs, Rhs>, Lhs, Rhs> base_type; typedef typename base_type::element_type element_type; using base_type::lhs_; using base_type::rhs_; public: // v[i]みたいに呼ばれたときに計算して返す typename base_type::element_type operator[](std::size_t i) const { return lhs_[i] + rhs_[i]; } vector_add(Lhs const & lhs, Rhs const & rhs) : base_type(lhs, rhs) {} }; // operator+ template<class Lhs, typename LElem, class Rhs, typename RElem> vector_add<Lhs, Rhs> operator+(vector_base<Lhs, LElem> const & lhs, vector_base<Rhs, RElem> const & rhs) { return vector_add<Lhs, Rhs>(static_cast<Lhs const &>(lhs), static_cast<Rhs const &>(rhs)); } // あとなんかoperator-とかの似たような定義 ... int main() { using std::cout; using std::endl; vector<float> vec1(1.0f, 1.0f, 1.0f); vector<int> vec2(1.0f, 2.0f, 3.0f); vector<float> vec3 = vec1 + vec2; vec1 = vec2; cout << vec1[0] << endl; cout << vec1[1] << endl; cout << vec1[2] << endl; cout << vec3[0] << endl; cout << vec3[1] << endl; cout << vec3[2] << endl; return 0; }
promote_typeは二項演算を適用した後の型を特定するテンプレートで、boostのmplとtype traitsを使って実装。
色々削ぎ落として書いたのでv = v1 + v2 + v3...ぐらいのことしかできない。それと、正直4要素ぐらいではETしなくてもいいと思うが気にしてはいけない。目的はあくまでもテンプレートをこねくり回してETすることである。
詳しい説明はまた今度。もう眠い。
えんいー