### 16.5 C++ Interface Internals

A system of expression templates is used to ensure something like `a=b+c` turns into a simple call to `mpz_add` etc. For `mpf_class` the scheme also ensures the precision of the final destination is used for any temporaries within a statement like `f=w*x+y*z`. These are important features which a naive implementation cannot provide.

A simplified description of the scheme follows. The true scheme is complicated by the fact that expressions have different return types. For detailed information, refer to the source code.

To perform an operation, say, addition, we first define a “function object” evaluating it,

    struct __gmp_binary_plus
    {
      static void eval(mpf_t f, const mpf_t g, const mpf_t h)
      {
        mpf_add(f, g, h);
      }
    };

And an “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);
    }

The seemingly redundant `__gmp_expr<__gmp_binary_expr<…>>` is used to encapsulate any possible kind of expression into a single template type. In fact even `mpf_class` etc are `typedef` specializations of `__gmp_expr`.

Next we define assignment of `__gmp_expr` to `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());
    }

where `expr.val1` and `expr.val2` are references to the expression’s operands (here `expr` is the `__gmp_binary_expr` stored within the `__gmp_expr`).

This way, the expression is actually evaluated only at the time of assignment, when the required precision (that of `f`) is known. Furthermore the target `mpf_t` is now available, thus we can call `mpf_add` directly with `f` as the output argument.

Compound expressions are handled by defining operators taking subexpressions as their arguments, like this:

    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);
    }

And the corresponding specializations of `__gmp_expr::eval`:

    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());
    }

The expression is thus recursively evaluated to any level of complexity and all subexpressions are evaluated to the precision of `f`.