Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

Front Page / Tutorial: Metafunctions and Higher-Order Metaprogramming / Dimensional Analysis / Implementing Division

Implementing Division

Division is similar to multiplication, but instead of adding exponents, we must subtract them. Rather than writing out a near duplicate of plus_f, we can use the following trick to make minus_f much simpler:

struct minus_f
{
    template <class T1, class T2>
    struct apply
      : mpl::minus<T1,T2> {};
};

Here minus_f::apply uses inheritance to expose the nested type of its base class, mpl::minus, so we don't have to write:

typedef typename ...::type type

We don't have to write typename here (in fact, it would be illegal), because the compiler knows that dependent names in apply's initializer list must be base classes. [2] This powerful simplification is known as metafunction forwarding; we'll apply it often as the book goes on. [3]

[2]In case you're wondering, the same approach could have been applied to plus_f, but since it's a little subtle, we introduced the straightforward but verbose formulation first.
[3]Users of EDG-based compilers should consult the book's Appendix C for a caveat about metafunction forwarding. You can tell whether you have an EDG compiler by checking the preprocessor symbol __EDG_VERSION__, which is defined by all EDG-based compilers.

Syntactic tricks notwithstanding, writing trivial classes to wrap existing metafunctions is going to get boring pretty quickly. Even though the definition of minus_f was far less verbose than that of plus_f, it's still an awful lot to type. Fortunately, MPL gives us a much simpler way to pass metafunctions around. Instead of building a whole metafunction class, we can invoke transform this way:

typename mpl::transform<D1,D2, mpl::minus<_1,_2> >::type

Those funny looking arguments (_1 and _2) are known as placeholders, and they signify that when the transform's BinaryOperation is invoked, its first and second arguments will be passed on to minus in the positions indicated by _1 and _2, respectively. The whole type mpl::minus<_1,_2> is known as a placeholder expression.

Note

MPL's placeholders are in the mpl::placeholders namespace and defined in boost/mpl/placeholders.hpp. In this book we will usually assume that you have written:

#include<boost/mpl/placeholders.hpp>
using namespace mpl::placeholders;

so that they can be accessed without qualification.

Here's our division operator written using placeholder expressions:

template <class T, class D1, class D2>
quantity< 
    T
  , typename mpl::transform<D1,D2,mpl::minus<_1,_2> >::type
>
operator/(quantity<T,D1> x, quantity<T,D2> y)
{
   typedef typename 
     mpl::transform<D1,D2,mpl::minus<_1,_2> >::type dim;

   return quantity<T,dim>( x.value() / y.value() );
}

This code is considerably simpler. We can simplify it even further by factoring the code that calculates the new dimensions into its own metafunction:

template <class D1, class D2>
struct divide_dimensions
  : mpl::transform<D1,D2,mpl::minus<_1,_2> > // forwarding again
{};

template <class T, class D1, class D2>
quantity<T, typename divide_dimensions<D1,D2>::type>
operator/(quantity<T,D1> x, quantity<T,D2> y)
{
   return quantity<T, typename divide_dimensions<D1,D2>::type>(
      x.value() / y.value());
}

Now we can verify our "force-on-a-laptop" computation by reversing it, as follows:

quantity<float,mass> m2 = f/a;
float rounding_error = std::abs((m2 - m).value());

If we got everything right, rounding_error should be very close to zero. These are boring calculations, but they're just the sort of thing that could ruin a whole program (or worse) if you got them wrong. If we had written a/f instead of f/a, there would have been a compilation error, preventing a mistake from propagating throughout our program.