Previous: , Up: Internals   [Index]


16.5 C++インターフェースの内部構造

演算子定義のテンプレートシステムは,例えば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.val1expr.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の桁数と同じ精度で計算されます。