サンプルの動作確認バージョン [GCC4.4/1.41.0] [VC9/1.41.0]
だいぶ長いですが、よくある例をやってみました。 m(0, 1) で第0,1要素が取得できるような2×2の行列クラスの足し算を、 Expression Template を使って実装します。
#include <iostream>
#include <boost/proto/proto.hpp>
using namespace std;
using namespace boost;
namespace ETSample
{
//
// 今回使う Expression Template の構文。値か、加算式
// Grammar ::= _ | Grammar + Grammar
//
struct Grammar
: proto::or_<
proto::terminal<proto::_>,
proto::plus<Grammar, Grammar>
>{};
//
// MatrixDomain :: 上記の文法にしたがったETを処理するための色々
//
template<typename Expr> struct MatrixExpr;
struct MatrixDomain
: proto::domain<proto::generator<MatrixExpr>, Grammar> {};
//
// MatrixExpr :: 上記の文法に従ったETの式を表すオブジェクトの型
//
template<typename Expr>
struct MatrixExpr
: proto::extends<Expr, MatrixExpr<Expr>, MatrixDomain>
{
explicit MatrixExpr(const Expr& e)
: proto::extends<Expr, MatrixExpr<Expr>, MatrixDomain>(e) {}
};
//
// ETで作った式を評価するための評価器。これは第i,j要素の値を返す評価器
//
struct EvalCtxByIndex
: proto::callable_context<const EvalCtxByIndex>
{
typedef double result_type;
int i_, j_;
EvalCtxByIndex(int i, int j) : i_(i), j_(j) {}
// 行列の生データの場合は普通に添え字アクセス
template<typename Matrix>
double operator()(proto::tag::terminal, const Matrix& data) const
{
return data(i_,j_);
}
// 加算式の場合は足し算
template<typename E1, typename E2>
double operator()(proto::tag::plus, const E1& e1, const E2& e2) const
{
return proto::eval(e1, *this) + proto::eval(e2, *this);
}
};
//
// 行列クラス本体
//
class Matrix
{
private:
double m[2][2];
public:
Matrix(){cout<<"Created"<<endl;}
Matrix(const Matrix&){cout<<"Copied"<<endl;}
// 添え字アクセス演算子
double& operator()(int i, int j) { return m[i][j]; }
const double& operator()(int i, int j) const { return m[i][j]; }
// ETの式を受け取ったときの代入処理!
template<typename Expr>
Matrix& operator=( const Expr& expr )
{
for(int i=0; i<2; ++i)
for(int j=0; j<2; ++j) {
// ETの式を評価して値を得る
const EvalCtxByIndex ctx(i, j);
m[i][j] = proto::eval(proto::as_expr<MatrixDomain>(expr), ctx);
}
return *this;
}
};
// MatrixクラスをMatrixDomainのET式として使えるようにするおまじない
template<typename> struct IsMatrix : mpl::false_ {};
template<> struct IsMatrix<Matrix> : mpl::true_ {};
BOOST_PROTO_DEFINE_OPERATORS(IsMatrix, MatrixDomain)
}
int main()
{
ETSample::Matrix m;
m(0,0) = 1;
m(0,1) = 2;
m(1,0) = 3;
m(1,1) = 4;
m = m+m+m;
cout << m(0,0) << endl;
cout << m(0,1) << endl;
cout << m(1,0) << endl;
cout << m(1,1) << endl;
}
Created 3 6 9 12
m+m+m の評価時に中間の Matrix オブジェクトが生成されずに、一発で計算されています。
Expression Template 技法で式の処理をするための汎用ライブラリです。 要は、演算子の適用の時点ではそのまま計算するのではなく計算式を覚えておいて、 あとで式に対して色々な操作を加える、という手法が Expression Template です。 行列演算の中間データを減らすとか、Boost.Lambda などのように、 式から関数オブジェクトを作るなどの応用に使われます。 現在、SpiritのV2 や xpressive は既に proto ベースになっているそうです。