Previous: Raw Output Internals, Up: Internals [Index]
演算子定義のテンプレートシステムは,例えばa=b+c
という式を使うと自動的にmpz_add
関数を呼び出す,ということを保証するためのものです。mpf_class
クラスでは,例えばf=w*x+y*z
という式を計算するための内部一時変数に対しても,最終的な演算結果と同じ桁数の計算を行うようにしており,単純な実装では提供していない重要な機能と言えるでしょう。
実装は式とは異なるデータタイプを返してきたりしてややこしいので,以下ではこの仕組みを簡単に説明します。
加算などの演算を実行するには,まず関数オブジェクト(function object)を定義します。
struct __gmp_binary_plus { static void eval(mpf_t f, const mpf_t g, const mpf_t h) { mpf_add(f, g, h); } };
そして下記のように,加算表現オブジェクト(additive expression object)を生成します。
__gmp_expr<__gmp_binary_expr<mpf_class, mpf_class, __gmp_binary_plus> > operator+(const mpf_class &f, const mpf_class &g) { return __gmp_expr <__gmp_binary_expr<mpf_class, mpf_class, __gmp_binary_plus> >(f, g); }
一見冗長な__gmp_expr<__gmp_binary_expr<…>>
という表現ですが,一つのテンプレート型に全ての可能な式表現をカプセル化することができます。実際,他のmpf_class
等のクラスでも,__gmp_expr
テンプレートのデータ型独自の特殊化を行っています。
次に,__gmp_expr
に対してmpf_class
が使えるように代入を定義します。
template <class T> mpf_class & mpf_class::operator=(const __gmp_expr<T> &expr) { expr.eval(this->get_mpf_t(), this->precision()); return *this; } template <class Op> void __gmp_expr<__gmp_binary_expr<mpf_class, mpf_class, Op> >::eval (mpf_t f, mp_bitcnt_t precision) { Op::eval(f, expr.val1.get_mpf_t(), expr.val2.get_mpf_t()); }
ここでexpr.val1
と expr.val2
は式表現中の演算子に対して参照(リファレンス)を提供しています(ここで,expr
は__gmp_expr
に格納されている__gmp_binary_expr
のことです)。
このようにして,式表現は割当てられた時にだけ実際に計算され,そこで必要となる精度(f
に定義されている)も分かります。更に,計算ターゲットのmpf_t
データも使えるようになります。これでようやくmpf_add
関数を直接f
を使って実行できるようになります。
より複雑な式の場合は,細かい式に分割して扱うようにします。例えば下記の式は次のようになります。
template <class T, class U> __gmp_expr <__gmp_binary_expr<__gmp_expr<T>, __gmp_expr<U>, __gmp_binary_plus> > operator+(const __gmp_expr<T> &expr1, const __gmp_expr<U> &expr2) { return __gmp_expr <__gmp_binary_expr<__gmp_expr<T>, __gmp_expr<U>, __gmp_binary_plus> > (expr1, expr2); }
対応するf
の特殊化は次のようになります。
template <class T, class U, class Op> void __gmp_expr <__gmp_binary_expr<__gmp_expr<T>, __gmp_expr<U>, Op> >::eval (mpf_t f, mp_bitcnt_t precision) { // declare two temporaries mpf_class temp1(expr.val1, precision), temp2(expr.val2, precision); Op::eval(f, temp1.get_mpf_t(), temp2.get_mpf_t()); }
従って,式表現は再帰的に評価され,全ての分割された式表現はf
の桁数と同じ精度で計算されます。