 Boost :

From: Hugo Duncan (hugoduncan_at_[hidden])
Date: 2003-10-14 09:29:51

Deane Yang <deane_yang_at_[hidden]> wrote:

> Hugo Duncan wrote:
>> This is a problem with a library that we use here. It defines
>> a quantity as a numeric type and a set of dimensions
>>
>> template <typename T, typename Dimensions> class quantity;
>>
>> where dimensions contains a mpl type sequence, with each
>> element holding instances of
>>
>> template <
>> typename DimensionType
>> , long Numerator
>> , long Denominator=1
>> > struct dimension_power
>>
>> Each DimensionType is then assosciated with a unit.
>>
>
> This sounds like a very cool way to define a dimension/quantity/units
> library. Is there any chance an implementation of this could be made
> available? If not, could someone more fluent in MPL than me explain
> how you would multiply two different quantities?

To multiply two quantities is not so simple, and the trick is to
factor out the sperate operations required.

Given:
template <typename NumericType, typename Dimensions> class quantity;

each operator defined on quantity essentially has to carry out the
following:

i) Type promotion on the NumericType's involved in the expression.
ii) Compute resulting dimension
iii) Ensure consistent units

i) Type promotion on the NumericType's involved in the expression.

To do this you can define type computation classes, and define
tags for each operator Op that you want to support.

template <typename Op, typename Arg1>
struct op1 : public arithmetic_promotion<Arg1> {};

template <typename Op, typename Arg1, typename Arg2>
struct op2 : public promoted<Arg1, Arg2> {};

ii) Compute resulting dimension

Again, define type computation classes,

template <typename Dimensions1,typename Dimensions2>
struct plus_traits;

To implement this you can require that the typelist of
dimensional powers is sortable by the dimension tag. The
maths itself can be implemented by an MPL type rational
class.

iii) Make sure that the unit used for each of the dimensions
is consistent between operands. A simple way to do this
is to convert all the units for dimensions common to
multiple operands to match those in one of the operands.

This requires a little care, eg if you have an integral
seconds representation, and you convert to hours (say),
you will need a non-integral representation.

So, declaration for operaor+ looks something like:

template <typename T1, typename Dimensions1
, typename T2, typename Dimensions2>
quantity<
typename type_promotion::op2<type_promotion::op_plus,T1,T2>::type
, typename plus_traits<Dimensions1,Dimensions2>::type
>
operator+(const quantity<T1,Dimensions1>& x,
const quantity<T2,Dimensions2>& y);

> The cool thing about this approach is that you can multiply or
> divide any two quantities and create a new dimension automatically.
> The resulting typelist is a union of the original two typelists
> with the Numerator and Denominator suitably updated.

Yes, and there can even be a runtime version of the dimensions that
interoperates with the compile time version. Similarly the units
for each dimension can be fixed at compile time or run time.

The downside of this is that you start relying on this computation of
resulting dimension, so switching the dimensional checking off
becomes harder.

Working with compile time units elimates the need to do unit conversion
in iii) above, and is a good choice for computational simulations. It
dows require however some abilities to convert a quantity to arbitrary
units at runtime for user interface purposes.

> Given enough time, I might be able to figure out how to generate the
> resulting typelist from the two input typelists, but I'm happy to let
> someone provide some hints or even answers.

Hope that provides some hints, and is understandable.

Hugo