Boost.ProtoのGetting Startedを簡単に訳して解説してみる。
Boost.ProtoはExpression Templateのライブラリを作るためのライブラリです。
簡単な例を挙げるとこんな感じで使います。
#include <iostream> #include <boost/proto/proto.hpp> using namespace boost::proto; terminal<std::ostream&>::type cout_ = { std::cout }; template <class Expr> void evaluate(const Expr& expr) { eval(expr, default_context()); } int main() { evaluate(cout_ << "Hello World\n"); }
このプログラムの「cout_ << "Hello World\n"」の時点ではまだstd::coutは評価されず、
式の情報だけが保存されます。
boost::proto::eval()にその式を渡すことで評価されます。
式の型は、Expression Templateの複雑な型になってるので仕方なく関数テンプレートに渡していますが、C++0xのautoを使用すれば以下のように書けます。
const auto& expr = cout_ << "Hello World\n"; eval(expr, default_context());
boost::proto::display_expr()に式を渡せばExpression Treeを表示することもできます。
display_expr(cout_ << "Hello World\n");
shift_left( terminal(78506BCC) , terminal(Hello World ) )
これだとツリーっぽく見えないですね…。
「<<」で何個か連結すればそれっぽく見えます。
display_expr(cout_ << "Hello" << "," << " World\n");
shift_left( shift_left( shift_left( terminal(78506BCC) , terminal(Hello) ) , terminal(,) ) , terminal( World ) )
Boost.Protoを使用すれば、Boost.Lambdaのようなプレースホルダによる計算をユーザーが簡単に定義することができます。
まず、プレースホルダは以下のように定義します。
template <int I> struct placeholder {}; boost::proto::terminal<placeholder<0> >::type const _1 = {{}}; boost::proto::terminal<placeholder<1> >::type const _2 = {{}};
これを用意すれば、以下のような式が書けます。
(_1 * _2 / _2) + 100
次に、プレースホルダを評価するコンテキストを用意します。
class calculator_context : public boost::proto::callable_context<const calculator_context> { std::vector<double> args_; // 値を置き換えるためのプレースホルダ public: typedef double result_type; template <int I> double operator()(boost::proto::tag::terminal, placeholder<I>) const { return args_[I]; } void add(double value) { args_.push_back(value); } };
これで準備ができました。以下のように使用します。
calculator_context context; context.add(3); // _1の値は3 context.add(5); // _2の値は5 double result = boost::proto::eval(_1 * _2 / _2 + 100, context); std::cout << result << std::endl; // 103が出力される
また、「_1 * _2 / _2 + 100」をBoost.Lambdaのように関数オブジェクトとして使用するには
先ほど作成したcalculator_contextをラップした以下のようなクラスを用意します。
template <typename Expr> struct calculator; struct calculator_domain : boost::proto::domain<boost::proto::generator<calculator> > {}; template <typename Expr> struct calculator : boost::proto::extends<Expr, calculator<Expr>, calculator_domain> { typedef boost::proto::extends<Expr, calculator<Expr>, calculator_domain> base_type; calculator(Expr const &expr = Expr()) : base_type(expr) {} typedef double result_type; double operator()(double a1 = 0, double a2 = 0) const { calculator_context context; context.add(a1); context.add(a2); return eval(*this, context); } };
あとは、プレースホルダを用意するときにこのcalculatorクラスでラップした型にしてあげれば
calculator<boost::proto::terminal<placeholder<0> >::type> const _1; calculator<boost::proto::terminal<placeholder<1> >::type> const _2;
以下のように使用できます。
double result = (_1 * _2 / _2 + 100)(3.0f, 5.0f); std::cout << result << std::endl; // 103
Boost.Protoを使用すれば、Expression Templateのライブラリを作ろうと思ったときにサクっと書けそうです。