Hi all
I've been working on a compile-time units-of-measurement tracking class for C++ based on an article written at embedded.com. I'm having some trouble debugging so if anyone's got the answer to my errors, please let me know.
I want the program to correctly compile with valid unit operations, and to fail compile when invalid operations such as Length x = 5 * second + 3 * joule.
Here are the error messages I get at the moment (code follows):
--------------------------
------8<--
----------
----------
------
john@john ~/dsg-transient/units
$ make test
g++ -fmessage-length=0 -ggdb -DCURSES_H=1 -DHAVE_GETOPT -DHAVE_STRING_H -DVERSION=\"i686-pc-cygwin
-2.2\" -W -O3 -c -o u
nits.o units.cpp
In file included from units.cpp:1:
units.h:250: error: conversion from `Units<0, 1, 0, 0, 0>' to non-scalar type `Units<0, 2, 0, 0, 0>' requested
units.h:253: error: conversion from `Units<0, 2, 0, 0, 0>' to non-scalar type `Units<0, 3, 0, 0, 0>' requested
units.h:261: error: conversion from `Units<1, 0, 0, 0, 0>' to non-scalar type `Units<1, 1, -2, 0, 0>' requested
units.h:263: error: conversion from `Units<1, 1, -2, 0, 0>' to non-scalar type `Units<1, -1, -2, 0, 0>' requested
units.h:266: error: conversion from `Units<1, 1, -2, 0, 0>' to non-scalar type `Units<1, 2, -2, 0, 0>' requested
units.h:268: error: conversion from `Units<1, 2, -2, 0, 0>' to non-scalar type `Units<1, 2, -3, 0, 0>' requested
make: *** [units.o] Error 1
--------------------------
------8<--
----------
----------
------
It seems it's not using the multiplication-with-units method which is defined.
Here's the test program:
--------------------------
------8<--
----------
----------
------
#include "units.h"
#include <iostream>
#include <iomanip>
#include <getopt.h>
using namespace std;
int main(int argc, char *argv[]){
try{
Velocity v = 60 * kilometre / hour;
Length x = 250 * kilometre;
Time t = x / v;
cerr << "The time taken to travel " << x << " at speed " << v << " is " << t << endl;
}catch(...){
cerr << "FATAL ERROR: unknown";
exit(1);
}
}
--------------------------
------8<--
----------
----------
------
Here's the header file with the unit tracking class template:
--------------------------
------8<--
----------
----------
------
/// UNIT CHECKING / TRACKING CLASS
/**
Adapted from code given by Rettig at
http://www.embedded.com/shared/printableArticle.jhtml?articleID=9900094 Code (currently) downloadable from
http://www.embedded.com/code/2001code.htm Original Author: Christopher Rettig ( rettigcd@bigfoot.com )
*/
#ifndef UNITS_H
#define UNITS_H
#define CHECK_UNITS
#ifdef CHECK_UNITS
// All of the reinterpret casts are work-arounds to let us make
// val & Units(double) private without using friends
/**
Purpose:
Template class to create a 'Units' data type.
It checks that units are correct at *COMPILE* time.
It hides conversion constants.
It enforces self documenting code.
Compilers known compliant:
. GNU gcc 3.3.1 (cygming special)
*/
template<int M,int L,int T, int K, int I>
class Units {
public:
/// Initialiser
Units(double v) : val(v){}
/// Copy constructor
Units( const Units& u ) : val( u.val ){}
/// Assignment operator
const Units& operator=( const Units& u ){ val=u.val; return *this; }
// MULTIPLICATION AND DIVISION BY SCALARS
Units operator*( double d ) const { return val*d; }
Units operator/( double d ) const { return val/d; }
const Units& operator*=( double d ) { val*=d; return *this; }
const Units& operator/=( double d ) { val/=d; return *this; }
// ADDITION/SUBTRACTION WITH UNITS
Units operator+( const Units& u ) const { return val+u.val; }
Units operator-( const Units& u ) const { return val-u.val; }
Units& operator+=( const Units& u ) { val+=u.val; return *this; }
Units& operator-=( const Units& u ) { val-=u.val; return *this; }
Units operator-() const { return -val; }
// COMPARATIVE OPERATORS
bool operator==( const Units& u ) const { return val==u.val; }
bool operator!=( const Units& u ) const { return val!=u.val; }
bool operator< ( const Units& u ) const { return val< u.val; }
bool operator<=( const Units& u ) const { return val<=u.val; }
bool operator> ( const Units& u ) const { return val> u.val; }
bool operator>=( const Units& u ) const { return val>=u.val; }
/// Cast to scalar
/**
Not defined here because only unitless can cast to double
*/
operator double() const;
/// Multiplication with units
template< int mm, int ll, int tt, int kk, int ii >
Units< M+mm, L+ll, T+tt, K+kk, I+ii >
operator*( const Units<mm,ll,tt,kk,ii>& u ) const {
//Units<M+m, L+l, T+t, K+k, I+i> r;
//*reinterpret_cast<double
*>(&r) = val * *reinterpret_cast<const double*>(&u);
return Units<M+mm,L+ll,T+tt,K+kk,
I+ii>(u.va
l);
//return r;
}
/// Division with units
template< int m, int l, int t, int k, int i >
Units< M-m, L-l, T-t, K-k, I-i>
operator/( const Units<m,l,t,k,i>& u ) const {
Units<M-m, L-l, T-t, K-k, I-i> r;
*reinterpret_cast<double*>
(&r) = val / *reinterpret_cast<const double*>(&u);
return r;
}
private:
double val;
// used by */+- to make returning values easy
//Units( double v ):setval(v){}
};
/// Casting to double
/**
Only defined for unitless types
*/
inline Units<0,0,0,0,0>::operator
double() const {
return val;
}
/// Scalar multiplication
template<int M, int L, int T, int K, int I>
inline Units< M, L, T, K, I >
operator*( double d, const Units< M, L, T, K, I > &u) {
return u*d;
}
/// Scalar division
template<int M, int L, int T, int K, int I >
inline Units< -M, -L, -T, -K, -I >
operator/( double d, const Units< M, L, T, K, I >& u) {
Units< -M, -L, -T, -K, -I > r;
*reinterpret_cast<double*>
(&r) = d / *reinterpret_cast<const double*>(&u);
return r;
}
//------------------------
--------
// BASE MEASURES
typedef Units< 1, 0, 0, 0, 0 > Mass;
typedef Units< 0, 1, 0, 0, 0 > Length;
typedef Units< 0, 0, 1, 0, 0 > Time;
typedef Units< 0, 0, 0, 1, 0 > Temperature;
typedef Units< 0, 0, 0, 0, 1 > Current;
//------------------------
--------
// DERIVED MEASURES
typedef Units< 0, 2, 0, 0, 0 > Area;
typedef Units< 0, 3, 0, 0, 0 > Volume;
typedef Units< 1, 1, -2, 0, 0 > Force;
typedef Units< 1, -1, -2, 0, 0 > Pressure;
typedef Units< 0, 1, -1, 0, 0 > Velocity;
typedef Units< 0, 1, -2, 0, 0 > Acceleration;
typedef Units< 1, 2, -2, 0, 0 > Torque;
typedef Units< 1, 2, -2, 0, 0 > Energy;
typedef Units< 1, 2, -3, 0, 0 > Power;
typedef Units< 0, 2, -2, 0, 0 > SpecificEnergy;
typedef Units< 1, 2, -2, -1, 0 > Entropy;
typedef Units< 0, 2, -2, -1, 0 > SpecificEntropy;
typedef Units< 1, 1, -3, 0, -1 > Conductivity;
typedef Units< 0, 3, -1, 0, 0 > Flowrate;
// Electrical
typedef Units< 0, 0, 1, 0, 1 > Charge;
typedef Units< 1, 2, -3, 0, -1 > ElecPotential;
typedef Units< 1, 2, -4, 0, -2 > Capacitance;
typedef Units< 1, 2, -3, 0, -2 > Resistance;
typedef Units<-1, -2, 3, 0, 2 > Conductance;
#else
// Fancy Units template becomes just a scalar
typedef double Units;
//------------------------
--------
// BASE MEASURES
typedef Units Mass;
typedef Units Length;
typedef Units Time;
typedef Units Temperature;
typedef Units Current;
//------------------------
--------
// DERIVED MEASURES
typedef Units Area;
typedef Units Volume;
typedef Units Force;
typedef Units Pressure;
typedef Units Velocity;
typedef Units Acceleration;
typedef Units Torque;
typedef Units Energy;
typedef Units Power;
typedef Units SpecificEnergy;
typedef Units Entropy;
typedef Units SpecificEntropy;
typedef Units Flowrate;
// Electrical
typedef Units Charge;
typedef Units ElecPotential;
typedef Units Capacitance;
typedef Units Resistance;
typedef Units Conductance;
#endif // CHECK_UNITS
//------------------------
----------
----------
--
// Define category bases
const Mass kilogram=Mass(1.0);
const Length metre=Length(1.0);
const Time second=Time(1.0);
const Temperature Kelvin=Temperature(1.0);
const Current ampere=Current(1.0);
//------------------------
----------
--
// SOME ALTERNATIVE NAMES
typedef Velocity Speed;
typedef Length Distance;
typedef Energy Heat;
typedef Heat Work;
typedef Pressure Stress;
//------------------------
----------
--
// SI MULTIPLIERS
const double Tera=1e12;
const double Giga=1e9;
const double Mega=1e6;
const double kilo=1e3;
const double hecta=1e2;
const double Deca=10;
const double deci=0.1;
const double centi=1e-2;
const double milli=1e-3;
const double micro=1e-6;
//------------------------
----------
--
// COMMON MEASURES (SI)
const Mass gram = milli * kilogram;
const Length centimetre = metre / 100.0;
const Length kilometre = 1000.0 * metre;
const Area metre2 = metre * metre;
const Area hectare = (100*metre)*(100*metre);
const Volume metre3 = metre2 * metre;
const Volume litre = milli * metre3;
const Volume centimetre3 = (centi*metre)*(centi*metre
)*(centi*m
etre);
const Time minute = 60.0 * second;
const Time hour= 60.0 * minute;
const Time day = 24.0 * hour;
const Force Newton = kilogram * metre / ( second * second );
const Pressure Pascal = Newton / (metre * metre );
const Pressure bar = 100.0 * kilo * Pascal;
const Pressure MPa = Mega * Pascal;
const Energy Joule = Newton * metre;
const Power Watt = Joule / second;
//const ElecPotential volt = Watt / ampere;
//const Charge Coulomb = ampere * second;
//const Capacitance Farad = volt / Coulomb;
//const Resistance Ohm = volt / ampere;
//------------------------
----------
--
// COMMON MEASURES (NON-SI)
// ...
//------------------------
----------
--
// THERMODYNAMIC MEASURES
const SpecificEnergy kJ_kg = kilo * Joule / kilogram;
const SpecificEntropy kJ_kgK = kilo * Joule / kilogram / Kelvin;
#endif // UNITS_H
--------------------------
----------
----8<----
----------
----------
--------