From: Dean Foster (foster_at_[hidden])
Date: 2001-09-05 13:56:36
I just joined the list a day or so ago. My reasoning for joining was
that I have a "units" library that I thought might be of more general
interest. I was going to put it up on source forge, but this might be
a more appropiate place.
I've been using it for about a year (but only under g++ only). I've
found it very useful, but it is far from polished.
I attached my current "README" at the end to describe the approach
I've been taking.
Dean Foster dean_at_[hidden]
Statistics, Wharton, U. Penn 215 898 8233
Philadelphia PA 19104-6302 http://diskworld.wharton.upenn.edu
The goal of this class is to make a strongly typed collection of
"doubles" in C++. So if someone (for example JPL/Boeing) try to add
feet to meters a conversion will be done first. But more importantly,
if someone tries to add meters to kg's, a compile time error will
A primary design goal is that there should be no speed costs or space
costs in using these classes. This is theoretically true, but g++
(gcc 2.95.2) doesn't handle things complete correctly, so there is a
bit of overhead. This will hopefully be optimized away eventually.
Here is a mininal but completely working example of using the units
typedef Unit<Simple_units, 1 > foot;
typedef Unit<Simple_units, 0, 1 > pound;
typedef Unit<Simple_units, 0, 0, 1> second;
typedef Unit<Simple_units, 0 > pure; // define the dimensionless unit
class Simple_units: public System
virtual std::string dim_name(int) const;
Simple_units::dim_name(int d) const
if(d == 0)
if(d == 1)
if(d == 2)
return "not used";
(See the actual example.h and example.cc for a slightly more
sophicated example that includes miles and yards.)
Now one can do the following:
const foot zero_feet = 0;
const foot FOOT;
foot length = 3 * foot();
foot width = 10 * FOOT;
cout << "Perimeter of square is: " << 2 * (length + width) << endl;
// output: Perimeter of square is: 26 feet
cout << "Area of square is: " << (length * width) << endl;
// output: Area of square is: 30 feet^2
p = length; // generates a compile time error
p = 10 * pound() * length / width; // is legal since it is conformal
cout << "Anyone know what a foot-lbs measures? " << (p * width) << endl;
// output: Anyone know what a foot-lbs measures? 100 (foot lbs)
(see example.test.cc for more sophicated examples.)
The key motivation is the "units" computer under unix. It happily
converts from almost any unit to almost any other unit. If you look
at /usr/lib/units.lib, you will see that it is done with 9 basic
dimensions. This is the same concept we are using.
In the Basic_units template class, there are currently 5 place holders
for dimensions. (So we can't represent unix_units yet.) Before two
items can be added together, they must match on all of these
dimensions. If two things are multiplied, then all of these dimensions
are added together.
This allows checks for conformal errors. But, it wouldn't allow for
different names. So a user would have to pick a single unit for
length, and represent EVERYTHING in in terms of those units. This
wouldn't be so bad in the M/K/S or C/G/S systems, but it would be hell
in the English system. Further, it wouldn't allow conversions from
English to SI.
So we have a template class called "Constants". This is where most
user readable numbers should be represented. So one could have energy
called kg m^2/s^2, joules, ergs, calories, BTU, etc and all would
inter-convert. Of course, they would be understood as the same
basic_unit of kg m^2/s^2.
The current weakness is that there really isn't much smarts in the IO.
It would be nice to be able to read in many different unit types and
convert them on the fly (as long as they are conformal). But right
now, it requires it to read in exactly as it prints out--no
STYLE: WHAT IS A UNIT?
How strongly typed should a unit be? Certainly feet and lbs should be
two different concepts. But should length and widths be two different
units? This is of course a personal decision that has no wrong
answers. If you make the types too broad (say by using the ideas of
general relativity to allow conversions from distance to time via a
speed of light conversion and hence making distance and time the same
unit) then some errors that could be caught at compile time will have
to be caught when the logic error is discovered at run time. If you
make the types too narrow, (say by having distances divided up into
length, widths, heights, radii, perimeter, etc) then you don't get the
advantage of multiplying two distances together and getting an
area--instead you get a variety of different areas--a (width x height)
a (radius^2) etc none of which are convertible from one to the other.
I think the way to make this decision is to remember that we are
programmers first and philosophers second. For example, in a banking
program, there many be many different flavors of cash, but all
physical units (feet, lbs, etc) might be lumped into a single
physical_unit category. Then the price per pound would be
$/physical_unit. The key thing is that a simple program called
total_cost that accepts two numbers, will know which is the quantity
and which is the price and will return an appropriate dollar amount.
So if you can divide the doubles that you have in your code into 10
different equally likely categories, then 9/10 errors of calling with
the wrong parameter will be caught. Also 9/10 interchange errors will
be caught. If you increased this to 100s of different categories,
that would only slightly increase the number of errors caught at
compile time--but it would make the code much more complex.
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk