From: Deane Yang (deane_yang_at_[hidden])
Date: 2007-02-16 15:18:58
Peter Dimov wrote:
>> Matthias Schabel <boost <at> schabel-family.org> writes:
>>> But in general we should assume that constructs like this are legit:
>>> where the system of the result type defines the conversions
>>> to get the conversion factor...this is unambiguous in all cases,
> This is possible, but is it a good idea? In C++ expressions are
> context-independent; that is, x * y * z * w always has a specific type T,
> regardless of whether it's assigned to an int, used to construct a
> quantity<SI::volume>, passed to a function taking double, or passed to a
> function template taking X const& x, where X is a template parameter. This
> has the advantage that it scales well. Consider
> ( 1.0 * SI::meters + 1.0 * imperial::feet ) * ( 2.0 * CGS::centimeters + 1.0
> * imperial::feet )
> and other complex expressions. Pushing down a result type may be hard. If
> your result type is nautical::miles * imperial::miles, how do you decide
> which of the two branches needs to get the nautical::miles? And this is a
> relatively simple example.
<Warning. This is a possibly totally misuided rant>
Yuck. Please forgive me for stepping into the middle of this discussion
without having read any of the previous discussion carefully (I have
been scanning all of the messages as they were posted), but the fact
that this discussion is even taking place makes me feel like everyone is
going down the wrong path.
Does anyone really have software with code like
( 1.0 * SI::meters + 1.0 * imperial::feet ) * ( 2.0 * CGS::centimeters +
1.0 * imperial::feet )
std::cout << x + y << '\n';
(where x and y have the same dimension but different units)
Does anyone really want a library that makes this possible?
I'm far from an expert in software engineering. Nor do I know exactly
what others are using the dimension/units library for. But the last
thing I want is a library that allows code like this.
Let me try to make some points (which I concede are totally self-centered):
1) To me, the purpose of such a library is *not* to be able to mix
different units of the same dimension inside a single expression. I'm
sorry, but I feel that the existence of an arithmetic expression with
different units for the same dimension is a clear sign of poor software
design. I would certainly redesign my code to eliminate anything like
this. There are just too many possible pitfalls.
Since I can't rule out the possibility of needing a mixed-units
expression, I would simply allow it only through explicit
2) For me, one purpose of a dimensions/units library is *code reuse*. It
allows me to implement algorithms generically (using templates) without
making any assumptions of what the units used for each dimension are,
except to say that they are consistent with each other (so if distance
is in miles and time is in seconds, then speed must be in miles per
second). I can then use the same code in different settings that use
different sets of units and even different sets of dimensions(!).
3) Another purpose is compile-time checking that all of the formulas in
my algorithm are logically consistent regarding dimensions/units. Again,
this requires no conversion between different units for the same dimension.
4) Another thing I like to do is implementing a generic interface for a
code module. So even if within the code module I have locked in the
units used for all the computations within the module, I want to be able
to pass in the input values in any units and not just the ones actually
used in the implementation. So what I do is I implement templated
constructors or functions and the first thing the function/constructor
does is convert the input value into the value using the internal units.
For a constructor, this can be done in the initialization list. The
dimensions/units types make this very easy to detect at compile-time
what the units of the input value are and performing the appropriate
So my code often looks something like this:
typedef meters internal_distance_type;
typedef kilograms internal_mass_type;
template <class DistanceType, class MassType>
ComputeObject(DistanceType distance, MassType mass)
...(do computations using internal units)...
// Output functions
template <class MassType>
MassType some_calculated_mass() const
...(do calculations using internal types)...
5) If I need runtime chosen units (and assuming that the conversion
factors have been locked in at compile-time), then this is rather easy
to implement as a layer above the interface described in 4). But it does
require using dynamic types that have nothing to do with the
dimension/units library, in which units are set at compile-time.
6) In practice, I now write all my mathematical algorithms in templated
classes and functions that make no assumptions about what the dimensions
and units are. Only when I write library and application interfaces do I
"lock" in what the units and dimensions are. All of the code dealing
with specific units or dimensions are in the interface code only.
(Is it possible that this is where I, a mathematician, differ from the
physicists? Physicists feel that various formulas, though valid for
different units, are still tied to specific dimensions. I don't. The
same formula can be useful in different settings, where the dimensions
involved are completely different. The heat equation and its solution is
a spectacular example of this; it is certainly important in physics, but
it is equally important in finance but using different dimensions. So I
would not want my code to lock in what the dimensions are, never mind
the units. The irony is that this means I want a units library and the
physicists seem to prefer a dimensions library.)
So this means that for *me* and, I guess, *me* only:
1) I do not need a library that insists on defining some "system of
units" to which everything gets converted.
2) I am strongly against implicit conversion of units. If any line of my
code has something in the wrong set of units, it means to me I screwed
up the interface of the function that line of code is in. For me, units
conversion *only* occurs at the interface of a function or constructor
and never anywhere after that.
At best, it appears to me that how I use the dimension/unit library is
very different from what everybody is doing. So I will retire from the
discussion for now. If I have missed the point entirely, I apologize and
please feel free to pretend my post never existed.
Some day (probably long after I have retired), I'll try to make my
library fit for public viewing and post it.
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk