On Saturday, 6 October 2012 at 19:40:10 UTC, Arlen wrote:
Besides, the math behind it is sound and and many years of hard work has gone into it.

Also, it is extremely complicated by the fact that it somehow has to work withing the boundaries of C++ and boost::mpl and friends.

I do want to mention that the distinction between dimension and unit is important.

Why? From a theorist standpoint, there of course is a distinction between the two, yes. In practice, however, a dimension is simply a connected component in the graph of (non-composite) units, where the edges represent known conversions, i.e. a dimension is just a name for a set of units which can converted into each other. The concept of dimensions arises naturally, and no rigor is lost by not explicitly having it; there is no need to force the redundancy on the programmer.

In fact, my library was also based on dimensions originally, but at some point, they became an annoyance in the code I wrote using it, and I discovered that I could do perfectly fine without them.

I rather see this:

Quantity!(length, float) a = 2 * metre;

than this:

Quantity!(metre, float) a = 2 * metre;

Really? The first has the problem that you have absolutely no idea what a is really represented like in memory – you need to have a canonical base unit all other units are automatically converted to. One consequence of that is that you are forced to do a lot of unneeded conversions if you can't completely work in that base unit, for example for interfacing with code that doesn't use your unit system. You also can't just transparently tack a unit on a quantity in places where the actual value is important, think serialization, ABI boundaries, etc. Not being able to chose your representation might also lead to precision issues if you are working with base types of limited width.

In contrast, the concept of dimensions is easy to implement in terms of units. For example, you could just define 'isLength' as 'canConvert!meter' (it will accept any unit which can somehow, maybe not directly, converted into meters), and then implement a function like this:

---
auto squareArea(T)(T side) if (isQuantity!T && isLength!(T.unit)) {
  return side * side;
}
---

squareArea now works transparently and without overhead with all unit types. Of course, you could also define the parameter as just Quantity!(meter, T) or Quantity!(meter, float) if for some reason you want to, for example because you are only working in meters anyway and you don't want to have a template function to avoid the (compile-time) overhead.

I think the main reason why Boost.Units went with dimensions is that having a distinguished "canonical" unit for each dimension relieves the burden on the implementation in so far as you never have to synthesize code for arbitrary conversions, as your data is always stored in that canonical unit.

David

Reply via email to