physical units checked at compile time (yet lacking IFTI capabilities)

Norbert Nemec Norbert at Nemec-online.de
Sun Apr 23 10:06:10 PDT 2006


I just wanted to try some ideas based on templates. After a long odyssee
through the details of D template programming, I finally succeeded to
arrive at a working piece of code, even though it is still a long way
from where I hoped to arrive.

I think it is interesting to share this experience for several reasons:

* It shows a promising idea how to handle and check physical dimensions
along with unit conversions at compile time.
* It gives a good example where IFTI would be crucial.
* It illustrates a point where IFTI does not work yet.
* Somebody may be able to give me further ideas about how to go on.

***********************************************

Let me explain:

The ultimate goal was to handle physical dimensions as compile time.
Basically, I would then be able to write code like:

-----
| mass m = 0.5 * ounce;
| velocity v = 1.0 * km / hour;
| energy E_kin = 0.5 * m * v*v;
| printf("E_kin = %g kcal\n",E_kin/kcal);
-----

and so on, where all the dimensions are checked at compile time and all
necessary conversions are done automatically.

I started out with the basic idea:

-------------------
| struct physical_quantity(
| 	int m, // length
| 	int s, // time
| 	int kg // mass
| 	// ignore A, K, mol and cd for the moment
| ) {
| 	double value;
| }
|
| alias physical_quantity!(1,0,0) length;
| alias physical_quantity!(0,1,0) time;
| alias physical_quantity!(0,0,1) mass;
|
| const length meter    = { value: 1.0 };
| const time   second   = { value: 1.0 };
| const mass   kilogram = { value: 1.0 };
-------------------

which could be continued like:

-------------------
| alias physical_quantity!(1,-1,0) velocity;
| alias physical_quantity!(2,-2,1) energy;
|
| const time minute = { value: 60.0 };
| const time hour = { value: 3600.0 };
| const velocity speedoflight = { value: 299792458 };
| const energy Joule = { value: 1.0 };
| const energy kcal = { value: 4185.0 };
-------------------

So far, everything worked fine. Now, I wanted to continue defining the
multiplication such that I could do, for example:

-------------------
| physical_quantity!(1,4,-2) v1 = { value: 1.0 };
| physical_quantity!(2,-4,1) v2 = { value: 1.0 };
| physical_quantity!(3,0,-1) prod = v1 * v2;
| // internally, equivalent to: prod.value = v1.value * v2.value;
-------------------

but I failed horribly trying to define this multiplication.

The most promising try so far was:

-------------------------------
| struct physical_quantity(
|     int m,
|     int s,
|     int kg
| ) {
|     double value;
|
|     const int _m = m;
|     const int _s = s;
|     const int _kg = kg;
|
|     template opMul(T) {   // line 12
|         physical_quantity!(m+T._m,s+T._s,kg+T._kg) opMul(T other) {
|             physical_quantity!(m+T._m,s+T._s,kg+T._kg) res;
|             res.value = value*other.value;
|             return res;
|         }
|     }
| };
|
| int main(char[][] args)
| {
|     physical_quantity!(1,0,0) v1; v1.value = 1.0;
|     physical_quantity!(0,1,0) v2; v2.value = 1.0;
|     physical_quantity!(1,1,0) prod = v1*v2;      // line 25
|     printf("%f\n",prod.value);
|     return 0;
| }
--------------------------------

which fails with the compiler error:

------------
| units_minimal.d(25): incompatible types for ((v1) * (v2)):
'physical_quantity' and 'physical_quantity'
| units_minimal.d(25): 'v1' is not an arithmetic type
| units_minimal.d(25): 'v2' is not an arithmetic type
| units_minimal.d(25): cannot implicitly convert expression ((v1) *
(v2)) of type physical_quantity to physical_quantity
------------

My guess was that the problem is with the operator overloading, and
indeed, replacing line 25 by
-------------
|     physical_quantity!(1,1,0) prod = v1.opMul(v2);      // line 25
-------------

changes the compiler error to:

------------
| units_minimal.d(12): template
units_minimal.physical_quantity!(1,0,0).physical_quantity.opMul(T)
templates don't have properties
| units_minimal.d(25): function expected before (), not v1 of type
physical_quantity
| units_minimal.d(25): cannot implicitly convert expression ((v1)((v2)))
of type int to physical_quantity
------------

My next guess was, that the IFTI causes the problem, so I changed lines
12 and 25 to

------------
|     template T_opMul(T) {   // line 12
------------
|     physical_quantity!(1,1,0) prod =
v1.T_opMul!(typeof(v2)).opMul(v2);      // line 25
------------

and now the program worked correctly.

Of course, in this state, the whole code is far from useful. Without
IFTM and operator overloading, the whole point of the library is moot:
allowing readable code with compile-time error checking.

Even if IFTI and operators would work, there is, of course, some way to
go for a working library, but I am confident, that this it will be
possible to go that way.

Greetings,
Norbert



More information about the Digitalmars-d mailing list