Front Page / Tutorial: Metafunctions and Higher-Order Metaprogramming / Dimensional Analysis / Representing Dimensions

Representing Dimensions

An international standard called Système International d'Unites (SI), breaks every quantity down into a combination of the dimensions mass, length (or position), time, charge, temperature, intensity, and angle. To be reasonably general, our system would have to be able to represent seven or more fundamental dimensions. It also needs the ability to represent composite dimensions that, like force, are built through multiplication or division of the fundamental ones.

In general, a composite dimension is the product of powers of fundamental dimensions. [1] If we were going to represent these powers for manipulation at runtime, we could use an array of seven ints, with each position in the array holding the power of a different fundamental dimension:

typedef int dimension[7]; // m  l  t  ...
dimension const mass      = {1, 0, 0, 0, 0, 0, 0};
dimension const length    = {0, 1, 0, 0, 0, 0, 0};
dimension const time      = {0, 0, 1, 0, 0, 0, 0};
...
[1]Divisors just contribute negative exponents, since 1/x = x-1.

In that representation, force would be:

dimension const force  = {1, 1, -2, 0, 0, 0, 0};

that is, mlt-2. However, if we want to get dimensions into the type system, these arrays won't do the trick: they're all the same type! Instead, we need types that themselves represent sequences of numbers, so that two masses have the same type and a mass is a different type from a length.

Fortunately, the MPL provides us with a collection of type sequences. For example, we can build a sequence of the built-in signed integral types this way:

#include <boost/mpl/vector.hpp>

typedef boost::mpl::vector<
     signed char, short, int, long> signed_types;

How can we use a type sequence to represent numbers? Just as numerical metafunctions pass and return wrapper types having a nested ::value, so numerical sequences are really sequences of wrapper types (another example of polymorphism). To make this sort of thing easier, MPL supplies the int_<N> class template, which presents its integral argument as a nested ::value:

#include <boost/mpl/int.hpp>

namespace mpl = boost::mpl; // namespace alias
static int const five = mpl::int_<5>::value;

In fact, the library contains a whole suite of integral constant wrappers such as long_ and bool_, each one wrapping a different type of integral constant within a class template.

Now we can build our fundamental dimensions:

typedef mpl::vector<
   mpl::int_<1>, mpl::int_<0>, mpl::int_<0>, mpl::int_<0>
 , mpl::int_<0>, mpl::int_<0>, mpl::int_<0>
> mass;

typedef mpl::vector<
   mpl::int_<0>, mpl::int_<1>, mpl::int_<0>, mpl::int_<0>
 , mpl::int_<0>, mpl::int_<0>, mpl::int_<0>
> length;
...

Whew! That's going to get tiring pretty quickly. Worse, it's hard to read and verify: The essential information, the powers of each fundamental dimension, is buried in repetitive syntactic "noise." Accordingly, MPL supplies integral sequence wrappers that allow us to write:

#include <boost/mpl/vector_c.hpp>

typedef mpl::vector_c<int,1,0,0,0,0,0,0> mass;
typedef mpl::vector_c<int,0,1,0,0,0,0,0> length; // or position 
typedef mpl::vector_c<int,0,0,1,0,0,0,0> time;
typedef mpl::vector_c<int,0,0,0,1,0,0,0> charge;
typedef mpl::vector_c<int,0,0,0,0,1,0,0> temperature;
typedef mpl::vector_c<int,0,0,0,0,0,1,0> intensity;
typedef mpl::vector_c<int,0,0,0,0,0,0,1> angle;

Even though they have different types, you can think of these mpl::vector_c specializations as being equivalent to the more verbose versions above that use mpl::vector.

If we want, we can also define a few composite dimensions:

// base dimension:        m l  t ... 
typedef mpl::vector_c<int,0,1,-1,0,0,0,0> velocity;     // l/t
typedef mpl::vector_c<int,0,1,-2,0,0,0,0> acceleration; // l/(t2)
typedef mpl::vector_c<int,1,1,-1,0,0,0,0> momentum;     // ml/t
typedef mpl::vector_c<int,1,1,-2,0,0,0,0> force;        // ml/(t2)

And, incidentally, the dimensions of scalars (like pi) can be described as:

typedef mpl::vector_c<int,0,0,0,0,0,0,0> scalar;