jdpipe
asked on
Debugging: compile-time units-of-measurement tracking class for C++
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<---- ---------- ---------- --------
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):
--------------------------
john@john ~/dsg-transient/units
$ make test
g++ -fmessage-length=0 -ggdb -DCURSES_H=1 -DHAVE_GETOPT -DHAVE_STRING_H -DVERSION=\"i686-pc-cygwin
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
--------------------------
It seems it's not using the multiplication-with-units method which is defined.
Here's the test program:
--------------------------
#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);
}
}
--------------------------
Here's the header file with the unit tracking class template:
--------------------------
/// 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
return Units<M+mm,L+ll,T+tt,K+kk,
//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*>
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
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*>
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
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
--------------------------
To get it compiled i had to change the following:
1. Turned all integer factors to double, e. g. (metre*100) --> (metre*100.)
2. Added member function getval()
double getval() const { return val; }
and changed u.val by u.getval(). (my compiler didn't allow access to private
member if there was a change in the template parameters).
3. In template operator/ () function i had to change
Units<M-m, L-l, T-t, K-k, I-i> r;
to
Units<M-m, L-l, T-t, K-k, I-i> r(u.getval());
Regards, Alex
1. Turned all integer factors to double, e. g. (metre*100) --> (metre*100.)
2. Added member function getval()
double getval() const { return val; }
and changed u.val by u.getval(). (my compiler didn't allow access to private
member if there was a change in the template parameters).
3. In template operator/ () function i had to change
Units<M-m, L-l, T-t, K-k, I-i> r;
to
Units<M-m, L-l, T-t, K-k, I-i> r(u.getval());
Regards, Alex
ASKER
Cheers Alex, I'll give that a whiz and get back to you.
Andrewjb... yes, I had thought of that. Celcius to Fahrenheit are a problem and so is Kelvin to Celcius because that's an additive conversion, not a multiplication factor like all the others. C to F likewise I guess... don't know how I'll tackle that one! Probably need to specialise the Units class somehow.
JP
Andrewjb... yes, I had thought of that. Celcius to Fahrenheit are a problem and so is Kelvin to Celcius because that's an additive conversion, not a multiplication factor like all the others. C to F likewise I guess... don't know how I'll tackle that one! Probably need to specialise the Units class somehow.
JP
ASKER
Hi Alex,
Still have trouble with this. I made the changes you suggested, but I'm still seeing the same problem. I'm not 100% sure that I understood your suggestions right, maybe you could have one more look? Also, what compiler did you try with?
I've had more success moving the operators out of the class template into their own method templates, but it's less succinct so I'd prefer this syntax from Rettig if possible.
If you could send back the code as it was when you got it to compile - that would probably show me where my mistake was... hopefully it's not a quirk of the GCC compiler...!?!
JP
Still have trouble with this. I made the changes you suggested, but I'm still seeing the same problem. I'm not 100% sure that I understood your suggestions right, maybe you could have one more look? Also, what compiler did you try with?
I've had more success moving the operators out of the class template into their own method templates, but it's less succinct so I'd prefer this syntax from Rettig if possible.
If you could send back the code as it was when you got it to compile - that would probably show me where my mistake was... hopefully it's not a quirk of the GCC compiler...!?!
JP
I found out i made another change in units.h, that seems to be crucial:
It's a forward declaration of class Units.
If i omit it i get similar error messages than you. I guess it is the template operator * that would need it.
The compiler is VC6 on NT 4.0, actually not known to be very compliant...
Regards, Alex
//------------------------ ---------- ---------- ---------- ---
#include "units.h"
#include <iostream>
#include <iomanip>
//#include <getopt.h> // DIDN'T HAVE IT BUT DIDN'T NEED IT
using namespace std;
int main(int argc, char *argv[]){
try{
Velocity v = 60. * kilometre / hour; // Made 60.
Length x = 250. * kilometre; // Same 250.
Time t = x / v;
cerr << "The time taken to travel " << x << " at speed " << v << " is " << t << endl;
}catch(...){
cerr << "FATAL ERROR: unknown";
exit(1);
}
return 0;
}
//------------------------ ---------- ---------- --------
#ifndef UNITS_H
#define UNITS_H
#define CHECK_UNITS
#ifdef CHECK_UNITS
// forward declaration !!!!
template<int M,int L,int T, int K, int I>
class 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; }
double getval() const { return 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.ge tval());
//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(u.getval());
*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 = (metre * 100.) * (metre * 100.);
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
//------------------------ ---------- ---------- ---------- ---
It's a forward declaration of class Units.
If i omit it i get similar error messages than you. I guess it is the template operator * that would need it.
The compiler is VC6 on NT 4.0, actually not known to be very compliant...
Regards, Alex
//------------------------
#include "units.h"
#include <iostream>
#include <iomanip>
//#include <getopt.h> // DIDN'T HAVE IT BUT DIDN'T NEED IT
using namespace std;
int main(int argc, char *argv[]){
try{
Velocity v = 60. * kilometre / hour; // Made 60.
Length x = 250. * kilometre; // Same 250.
Time t = x / v;
cerr << "The time taken to travel " << x << " at speed " << v << " is " << t << endl;
}catch(...){
cerr << "FATAL ERROR: unknown";
exit(1);
}
return 0;
}
//------------------------
#ifndef UNITS_H
#define UNITS_H
#define CHECK_UNITS
#ifdef CHECK_UNITS
// forward declaration !!!!
template<int M,int L,int T, int K, int I>
class 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; }
double getval() const { return 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
return Units<M+mm,L+ll,T+tt,K+kk,
//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(u.getval());
*reinterpret_cast<double*>
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
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*>
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 = (metre * 100.) * (metre * 100.);
const Volume metre3 = metre2 * metre;
const Volume litre = milli * metre3;
const Volume centimetre3 = (centi*metre)*(centi*metre
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
//------------------------
I made the forward declaration because i tried to add a friend declaration of the template class itself.
friend class Units<M, L, T, K, I>;
The friend should help to access the private data member, but it doesn't work. So, i removed the friend declaration but not the forward declaration.
Regards, Alex
friend class Units<M, L, T, K, I>;
The friend should help to access the private data member, but it doesn't work. So, i removed the friend declaration but not the forward declaration.
Regards, Alex
ASKER
Hi Alex
I tried your code verbatim but it still gives those errors with GCC. The following implementation works in GCC, with the operators defined outside the class. Some problem with the nested templates, has to be.
Try this with your compiler and if it works then I'm happy.
Cheers
JP
-------------------------- ---------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
#include <iostream>
#include <cmath>
#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.
Dev platform:
. GNU gcc 3.3.1 (cygming special)
*/
template<int M,int L,int T, int K, int I>
class Units {
public:
Units(){
val=0;
}
/// Initialiser
Units(double v){
setValue(v);
}
/// Copy constructor
Units( const Units& u ) : val( u.getValue() ){}
/// Assignment operator
const Units& operator=( const Units& u ){ val=u.getValue(); return *this; }
// MULTIPLICATION AND DIVISION BY SCALARS
const Units& operator*=( double d ) { val*=d; return *this; }
const Units& operator/=( double d ) { val/=d; return *this; }
// ACCUMULATION AND DIMINUTION
Units& operator+=( const Units& u ) { val+=u.getValue(); return *this; }
Units& operator-=( const Units& u ) { val-=u.getValue(); return *this; }
/// Cast to scalar
/**
Not defined here because only Units<0,0,0,0,0> can cast to double
*/
operator double() const;
double getValue() const {
return val;
}
// Value checking
bool isValid(){
return !isnan(val) && !isinf(val);
}
bool isnan(){
return isnan(val);
}
bool isinf(){
return isinf(val);
}
private:
double val;
// used by */+- to make returning values easy
void setValue(double v){
val=v;
}
};
/// Casting to double
/**
Only defined for unitless types
*/
inline Units<0,0,0,0,0>::operator double() const {
return val;
}
// MULTIPLICATION
template<int M, int L, int T, int K, int I, int m, int l, int t, int k, int i>
inline
Units<M+m, L+l, T+t, K+k, I+i>
operator *(const Units<M,L,T,K,I> u,const Units<m,l,t,k,i> v){
return Units<M+m, L+l, T+t, K+k, I+i>(u.getValue() * v.getValue());
}
template<int m, int l, int t, int k, int i>
inline
Units<m, l, t, k, i>
operator *(double u,const Units<m,l,t,k,i> v){
return Units<m, l, t, k, i>(u * v.getValue());
}
template<int M, int L, int T, int K, int I>
inline
Units<M, L, T, K, I>
operator *(const Units<M,L,T,K,I> u,double v){
return Units<M, L, T, K, I>(u.getValue() * v);
}
template<int m, int l, int t, int k, int i>
inline
Units<m, l, t, k, i>
operator *(int u,const Units<m,l,t,k,i> v){
return Units<m, l, t, k, i>(u * v.getValue());
}
template<int M, int L, int T, int K, int I>
inline
Units<M, L, T, K, I>
operator *(const Units<M,L,T,K,I> u,int v){
return Units<M, L, T, K, I>(u.getValue() * v);
}
// DIVISION
template<int M, int L, int T, int K, int I, int m, int l, int t, int k, int i>
inline
Units<M-m, L-l, T-t, K-k, I-i>
operator/(const Units<M,L,T,K,I> u,const Units<m,l,t,k,i> v){
return Units<M-m, L-l, T-t, K-k, I-i>(u.getValue() / v.getValue());
}
template<int M, int L, int T, int K, int I>
inline
Units<M, L, T, K, I>
operator/(const Units<M,L,T,K,I> u,double v){
return Units<M, L, T, K, I>(u.getValue() / v);
}
template<int m, int l, int t, int k, int i>
inline
Units<-m, -l, -t, -k, -i>
operator/(double u,const Units<m,l,t,k,i> v){
return Units<-m, -l, -t, -k, -i>(u / v.getValue());
}
template<int m, int l, int t, int k, int i>
inline
Units<-m, -l, -t, -k, -i>
operator/(int u,const Units<m,l,t,k,i> v){
return Units<-m, -l, -t, -k, -i>(u / v.getValue());
}
template<int M, int L, int T, int K, int I>
inline
Units<M, L, T, K, I>
operator/(const Units<M,L,T,K,I> u,int v){
return Units<M, L, T, K, I>(u.getValue() / v);
}
// COMPARISON
template<int M, int L, int T, int K, int I>
inline
bool
operator==(const Units<M,L,T,K,I> u,const Units<M,L,T,K,I> v){
return u.getValue()==v.getValue() ;
}
template<int M, int L, int T, int K, int I>
inline
bool
operator!=(const Units<M,L,T,K,I> u,const Units<M,L,T,K,I> v){
return u.getValue()!=v.getValue() ;
}
template<int M, int L, int T, int K, int I>
inline
bool
operator<(const Units<M,L,T,K,I> u,const Units<M,L,T,K,I> v){
return u.getValue()<v.getValue();
}
template<int M, int L, int T, int K, int I>
inline
bool
operator<=(const Units<M,L,T,K,I> u,const Units<M,L,T,K,I> v){
return u.getValue()<=v.getValue() ;
}
template<int M, int L, int T, int K, int I>
inline
bool
operator>=(const Units<M,L,T,K,I> u,const Units<M,L,T,K,I> v){
return u.getValue()>=v.getValue() ;
}
// ADDITION AND SUBTRACTION
template<int M, int L, int T, int K, int I>
Units<M,L,T,K,I>
operator+(const Units<M,L,T,K,I> u,const Units<M,L,T,K,I> v){
return Units<M,L,T,K,I>(u.getValu e()+v.getV alue());
}
template<int M, int L, int T, int K, int I>
Units<M,L,T,K,I>
operator-(const Units<M,L,T,K,I> u,const Units<M,L,T,K,I> v){
return Units<M,L,T,K,I>(u.getValu e()-v.getV alue());
}
template<int M, int L, int T, int K, int I>
Units<M,L,T,K,I>
operator-(const Units<M,L,T,K,I> u){
return Units<M,L,T,K,I>(-u.getVal ue());
}
// OUTPUT
template<int m, int l, int t, int k, int i>
inline
std::ostream& operator <<(std::ostream &os,const Units<m,l,t,k,i> &u){
os << u.getValue();
os.flags() & std::ios_base::showbase && os << " (dim)";
return os;
}
#define DEFINE_OUTPUT_METHOD(MM,LL ,TT,KK,II, UNITS) \
inline \
std::ostream& operator <<(std::ostream &os,const Units<MM,LL,TT,KK,II> &u){ \
os << u.getValue(); \
os.flags() & std::ios_base::showbase && os << " " << UNITS; \
return os; \
}
DEFINE_OUTPUT_METHOD(0,1,0 ,0,0,"m");
DEFINE_OUTPUT_METHOD(0,0,1 ,0,0,"s");
DEFINE_OUTPUT_METHOD(0,1,- 1,0,0,"m/s ");
DEFINE_OUTPUT_METHOD(1,0,0 ,0,0,"kg") ;
DEFINE_OUTPUT_METHOD(0,0,0 ,1,0,"K");
//------------------------ --------
// 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, -3, 0, 0, 0 > Density;
typedef Units<-1, 3, 0, 0, 0 > SpecificVolume;
typedef Units< 1, -1, -1, 0, 0> DynamicViscosity;
typedef Units< 1, 1, -3, -1, 0 > 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 Density;
typedef Units SpecificVolume;
typedef Units DynamicViscosity;
typedef Units Flowrate;
// Electrical
typedef Units Charge;
typedef Units ElecPotential;
typedef Units Capacitance;
typedef Units Resistance;
typedef Units Conductance;
#endif // CHECK_UNITS
//------------------------ ---------- ---------- --
// BASE UNITS FOR BASE MEASURES
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; // nice
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 Mass kg = kilogram;
const Length centimetre = metre / 100.0;
const Length kilometre = 1000.0 * metre;
const Area metre2 = metre*metre;
const Area hectare = (100.0*metre)*(100.0*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;
//------------------------ ---------- --
// HANDLING TEMPERATURE CONVERSIONS
//...
#endif // UNITS_H
I tried your code verbatim but it still gives those errors with GCC. The following implementation works in GCC, with the operators defined outside the class. Some problem with the nested templates, has to be.
Try this with your compiler and if it works then I'm happy.
Cheers
JP
--------------------------
/// 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
#include <iostream>
#include <cmath>
#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.
Dev platform:
. GNU gcc 3.3.1 (cygming special)
*/
template<int M,int L,int T, int K, int I>
class Units {
public:
Units(){
val=0;
}
/// Initialiser
Units(double v){
setValue(v);
}
/// Copy constructor
Units( const Units& u ) : val( u.getValue() ){}
/// Assignment operator
const Units& operator=( const Units& u ){ val=u.getValue(); return *this; }
// MULTIPLICATION AND DIVISION BY SCALARS
const Units& operator*=( double d ) { val*=d; return *this; }
const Units& operator/=( double d ) { val/=d; return *this; }
// ACCUMULATION AND DIMINUTION
Units& operator+=( const Units& u ) { val+=u.getValue(); return *this; }
Units& operator-=( const Units& u ) { val-=u.getValue(); return *this; }
/// Cast to scalar
/**
Not defined here because only Units<0,0,0,0,0> can cast to double
*/
operator double() const;
double getValue() const {
return val;
}
// Value checking
bool isValid(){
return !isnan(val) && !isinf(val);
}
bool isnan(){
return isnan(val);
}
bool isinf(){
return isinf(val);
}
private:
double val;
// used by */+- to make returning values easy
void setValue(double v){
val=v;
}
};
/// Casting to double
/**
Only defined for unitless types
*/
inline Units<0,0,0,0,0>::operator
return val;
}
// MULTIPLICATION
template<int M, int L, int T, int K, int I, int m, int l, int t, int k, int i>
inline
Units<M+m, L+l, T+t, K+k, I+i>
operator *(const Units<M,L,T,K,I> u,const Units<m,l,t,k,i> v){
return Units<M+m, L+l, T+t, K+k, I+i>(u.getValue() * v.getValue());
}
template<int m, int l, int t, int k, int i>
inline
Units<m, l, t, k, i>
operator *(double u,const Units<m,l,t,k,i> v){
return Units<m, l, t, k, i>(u * v.getValue());
}
template<int M, int L, int T, int K, int I>
inline
Units<M, L, T, K, I>
operator *(const Units<M,L,T,K,I> u,double v){
return Units<M, L, T, K, I>(u.getValue() * v);
}
template<int m, int l, int t, int k, int i>
inline
Units<m, l, t, k, i>
operator *(int u,const Units<m,l,t,k,i> v){
return Units<m, l, t, k, i>(u * v.getValue());
}
template<int M, int L, int T, int K, int I>
inline
Units<M, L, T, K, I>
operator *(const Units<M,L,T,K,I> u,int v){
return Units<M, L, T, K, I>(u.getValue() * v);
}
// DIVISION
template<int M, int L, int T, int K, int I, int m, int l, int t, int k, int i>
inline
Units<M-m, L-l, T-t, K-k, I-i>
operator/(const Units<M,L,T,K,I> u,const Units<m,l,t,k,i> v){
return Units<M-m, L-l, T-t, K-k, I-i>(u.getValue() / v.getValue());
}
template<int M, int L, int T, int K, int I>
inline
Units<M, L, T, K, I>
operator/(const Units<M,L,T,K,I> u,double v){
return Units<M, L, T, K, I>(u.getValue() / v);
}
template<int m, int l, int t, int k, int i>
inline
Units<-m, -l, -t, -k, -i>
operator/(double u,const Units<m,l,t,k,i> v){
return Units<-m, -l, -t, -k, -i>(u / v.getValue());
}
template<int m, int l, int t, int k, int i>
inline
Units<-m, -l, -t, -k, -i>
operator/(int u,const Units<m,l,t,k,i> v){
return Units<-m, -l, -t, -k, -i>(u / v.getValue());
}
template<int M, int L, int T, int K, int I>
inline
Units<M, L, T, K, I>
operator/(const Units<M,L,T,K,I> u,int v){
return Units<M, L, T, K, I>(u.getValue() / v);
}
// COMPARISON
template<int M, int L, int T, int K, int I>
inline
bool
operator==(const Units<M,L,T,K,I> u,const Units<M,L,T,K,I> v){
return u.getValue()==v.getValue()
}
template<int M, int L, int T, int K, int I>
inline
bool
operator!=(const Units<M,L,T,K,I> u,const Units<M,L,T,K,I> v){
return u.getValue()!=v.getValue()
}
template<int M, int L, int T, int K, int I>
inline
bool
operator<(const Units<M,L,T,K,I> u,const Units<M,L,T,K,I> v){
return u.getValue()<v.getValue();
}
template<int M, int L, int T, int K, int I>
inline
bool
operator<=(const Units<M,L,T,K,I> u,const Units<M,L,T,K,I> v){
return u.getValue()<=v.getValue()
}
template<int M, int L, int T, int K, int I>
inline
bool
operator>=(const Units<M,L,T,K,I> u,const Units<M,L,T,K,I> v){
return u.getValue()>=v.getValue()
}
// ADDITION AND SUBTRACTION
template<int M, int L, int T, int K, int I>
Units<M,L,T,K,I>
operator+(const Units<M,L,T,K,I> u,const Units<M,L,T,K,I> v){
return Units<M,L,T,K,I>(u.getValu
}
template<int M, int L, int T, int K, int I>
Units<M,L,T,K,I>
operator-(const Units<M,L,T,K,I> u,const Units<M,L,T,K,I> v){
return Units<M,L,T,K,I>(u.getValu
}
template<int M, int L, int T, int K, int I>
Units<M,L,T,K,I>
operator-(const Units<M,L,T,K,I> u){
return Units<M,L,T,K,I>(-u.getVal
}
// OUTPUT
template<int m, int l, int t, int k, int i>
inline
std::ostream& operator <<(std::ostream &os,const Units<m,l,t,k,i> &u){
os << u.getValue();
os.flags() & std::ios_base::showbase && os << " (dim)";
return os;
}
#define DEFINE_OUTPUT_METHOD(MM,LL
inline \
std::ostream& operator <<(std::ostream &os,const Units<MM,LL,TT,KK,II> &u){ \
os << u.getValue(); \
os.flags() & std::ios_base::showbase && os << " " << UNITS; \
return os; \
}
DEFINE_OUTPUT_METHOD(0,1,0
DEFINE_OUTPUT_METHOD(0,0,1
DEFINE_OUTPUT_METHOD(0,1,-
DEFINE_OUTPUT_METHOD(1,0,0
DEFINE_OUTPUT_METHOD(0,0,0
//------------------------
// 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, -3, 0, 0, 0 > Density;
typedef Units<-1, 3, 0, 0, 0 > SpecificVolume;
typedef Units< 1, -1, -1, 0, 0> DynamicViscosity;
typedef Units< 1, 1, -3, -1, 0 > 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 Density;
typedef Units SpecificVolume;
typedef Units DynamicViscosity;
typedef Units Flowrate;
// Electrical
typedef Units Charge;
typedef Units ElecPotential;
typedef Units Capacitance;
typedef Units Resistance;
typedef Units Conductance;
#endif // CHECK_UNITS
//------------------------
// BASE UNITS FOR BASE MEASURES
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; // nice
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 Mass kg = kilogram;
const Length centimetre = metre / 100.0;
const Length kilometre = 1000.0 * metre;
const Area metre2 = metre*metre;
const Area hectare = (100.0*metre)*(100.0*metre
const Volume metre3 = metre2 * metre;
const Volume litre = milli * metre3;
const Volume centimetre3 = (centi*metre)*(centi*metre
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;
//------------------------
// HANDLING TEMPERATURE CONVERSIONS
//...
#endif // UNITS_H
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
I'm a bit baffled as to how exactly it works when you take away those extra operators. It does work though. Would love to know why now... What is getting cast to what, etc?
JP
Here's my final version, for now:
My test program is at the end
-------------------------- ---------- 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
#include <iostream>
#include <cmath>
#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.
Dev platform:
. Win2k -- gcc version 3.3.1 (cygming special)
Tested on:
. NT4.0 -- VC++ 6.0
. Win2k -- gcc version 3.2.3 (mingw special 20030504-1)
. Linus -- gcc version 3.2.2 20030222 (Red Hat Linux 3.2.2-5)
*/
template<int M,int L,int T, int K, int I>
class Units {
public:
Units(){
val=0;
}
/// Initialiser
Units(double v){
setValue(v);
}
/// Copy constructor
Units( const Units& u ) : val( u.getValue() ){}
/// Assignment operator
const Units& operator=( const Units& u ){ val=u.getValue(); return *this; }
// MULTIPLICATION AND DIVISION BY SCALARS
const Units& operator*=( double d ) { val*=d; return *this; }
const Units& operator/=( double d ) { val/=d; return *this; }
// ACCUMULATION AND DIMINUTION
Units& operator+=( const Units& u ) { val+=u.getValue(); return *this; }
Units& operator-=( const Units& u ) { val-=u.getValue(); return *this; }
/// Cast to scalar
/**
Not defined here because only Units<0,0,0,0,0> can cast to double
*/
operator double() const;
double getValue() const {
return val;
}
// Value checking
bool isValid(){
return !isnan(val) && !isinf(val);
}
bool isnan(){
return isnan(val);
}
bool isinf(){
return isinf(val);
}
private:
double val;
// used by */+- to make returning values easy
void setValue(double v){
val=v;
}
};
/// Casting to double
/**
Only defined for unitless types
*/
inline Units<0,0,0,0,0>::operator double() const {
return val;
}
// MULTIPLICATION
template<int M, int L, int T, int K, int I, int m, int l, int t, int k, int i>
inline
Units<M+m, L+l, T+t, K+k, I+i>
operator *(const Units<M,L,T,K,I> u,const Units<m,l,t,k,i> v){
return Units<M+m, L+l, T+t, K+k, I+i>(u.getValue() * v.getValue());
}
template<int m, int l, int t, int k, int i>
inline
Units<m, l, t, k, i>
operator *(double u,const Units<m,l,t,k,i> v){
return Units<m, l, t, k, i>(u * v.getValue());
}
template<int M, int L, int T, int K, int I>
inline
Units<M, L, T, K, I>
operator *(const Units<M,L,T,K,I> u,double v){
return Units<M, L, T, K, I>(u.getValue() * v);
}
template<int m, int l, int t, int k, int i>
inline
Units<m, l, t, k, i>
operator *(int u,const Units<m,l,t,k,i> v){
return Units<m, l, t, k, i>(u * v.getValue());
}
template<int M, int L, int T, int K, int I>
inline
Units<M, L, T, K, I>
operator *(const Units<M,L,T,K,I> u,int v){
return Units<M, L, T, K, I>(u.getValue() * v);
}
// DIVISION
template<int M, int L, int T, int K, int I, int m, int l, int t, int k, int i>
inline
Units<M-m, L-l, T-t, K-k, I-i>
operator/(const Units<M,L,T,K,I> u,const Units<m,l,t,k,i> v){
return Units<M-m, L-l, T-t, K-k, I-i>(u.getValue() / v.getValue());
}
template<int M, int L, int T, int K, int I>
inline
Units<M, L, T, K, I>
operator/(const Units<M,L,T,K,I> u,double v){
return Units<M, L, T, K, I>(u.getValue() / v);
}
template<int m, int l, int t, int k, int i>
inline
Units<-m, -l, -t, -k, -i>
operator/(double u,const Units<m,l,t,k,i> v){
return Units<-m, -l, -t, -k, -i>(u / v.getValue());
}
template<int m, int l, int t, int k, int i>
inline
Units<-m, -l, -t, -k, -i>
operator/(int u,const Units<m,l,t,k,i> v){
return Units<-m, -l, -t, -k, -i>(u / v.getValue());
}
template<int M, int L, int T, int K, int I>
inline
Units<M, L, T, K, I>
operator/(const Units<M,L,T,K,I> u,int v){
return Units<M, L, T, K, I>(u.getValue() / v);
}
// COMPARISON
template<int M, int L, int T, int K, int I>
inline
bool
operator==(const Units<M,L,T,K,I> u,const Units<M,L,T,K,I> v){
return u.getValue()==v.getValue() ;
}
template<int M, int L, int T, int K, int I>
inline
bool
operator!=(const Units<M,L,T,K,I> u,const Units<M,L,T,K,I> v){
return u.getValue()!=v.getValue() ;
}
template<int M, int L, int T, int K, int I>
inline
bool
operator<(const Units<M,L,T,K,I> u,const Units<M,L,T,K,I> v){
return u.getValue()<v.getValue();
}
template<int M, int L, int T, int K, int I>
inline
bool
operator<=(const Units<M,L,T,K,I> u,const Units<M,L,T,K,I> v){
return u.getValue()<=v.getValue() ;
}
template<int M, int L, int T, int K, int I>
inline
bool
operator>=(const Units<M,L,T,K,I> u,const Units<M,L,T,K,I> v){
return u.getValue()>=v.getValue() ;
}
// ADDITION AND SUBTRACTION
template<int M, int L, int T, int K, int I>
Units<M,L,T,K,I>
operator+(const Units<M,L,T,K,I> u,const Units<M,L,T,K,I> v){
return Units<M,L,T,K,I>(u.getValu e()+v.getV alue());
}
template<int M, int L, int T, int K, int I>
Units<M,L,T,K,I>
operator-(const Units<M,L,T,K,I> u,const Units<M,L,T,K,I> v){
return Units<M,L,T,K,I>(u.getValu e()-v.getV alue());
}
template<int M, int L, int T, int K, int I>
Units<M,L,T,K,I>
operator-(const Units<M,L,T,K,I> u){
return Units<M,L,T,K,I>(-u.getVal ue());
}
// OUTPUT
template<int m, int l, int t, int k, int i>
inline
std::ostream& operator <<(std::ostream &os,const Units<m,l,t,k,i> &u){
os << u.getValue();
os.flags() & std::ios_base::showbase && os << " (dim)";
return os;
}
inline
std::ostream& operator <<(std::ostream &os,const Units<0,0,0,0,0> &u){
os << u.getValue();
return os;
}
#define DEFINE_OUTPUT_METHOD(MM,LL ,TT,KK,II, UNITS) \
inline \
std::ostream& operator <<(std::ostream &os,const Units<MM,LL,TT,KK,II> &u){ \
os << u.getValue(); \
os.flags() & std::ios_base::showbase && os << " " << UNITS; \
return os; \
}
DEFINE_OUTPUT_METHOD(0,1,0 ,0,0,"m");
DEFINE_OUTPUT_METHOD(0,0,1 ,0,0,"s");
DEFINE_OUTPUT_METHOD(0,1,- 1,0,0,"m/s ");
DEFINE_OUTPUT_METHOD(1,0,0 ,0,0,"kg") ;
DEFINE_OUTPUT_METHOD(0,0,0 ,1,0,"K");
//------------------------ --------
// 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, -3, 0, 0, 0 > Density;
typedef Units<-1, 3, 0, 0, 0 > SpecificVolume;
typedef Units< 1, -1, -1, 0, 0> DynamicViscosity;
typedef Units< 1, 1, -3, -1, 0 > 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 Density;
typedef Units SpecificVolume;
typedef Units DynamicViscosity;
typedef Units Flowrate;
// Electrical
typedef Units Charge;
typedef Units ElecPotential;
typedef Units Capacitance;
typedef Units Resistance;
typedef Units Conductance;
#endif // CHECK_UNITS
//------------------------ ---------- ---------- --
// BASE UNITS FOR BASE MEASURES
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; // nice
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 Mass kg = kilogram;
const Length centimetre = metre / 100.0;
const Length kilometre = 1000.0 * metre;
const Area metre2 = metre*metre;
const Area hectare = (100.0*metre)*(100.0*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;
//------------------------ ---------- --
// HANDLING TEMPERATURE CONVERSIONS
//...
#endif // UNITS_H
-------------------------- ---------- ---------- 8<-------- ---------- ---------- ----
#include "units.h"
#include <iostream>
#include <iomanip>
using namespace std;
int main(int argc, char *argv[]){
try{
cerr.flags(ios_base::showb ase);
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;
Velocity vv = 10 * kilometre / hour;
cerr << "What about if the speed is " << vv << " faster?" << endl;
v+=vv;
cerr << "ie " << v << endl;
Time t2 = x / v;
cerr << "Then it takes " << t << " to travel " << x << endl;
cerr << "That's an improvements of " << t-t2 << endl;
cerr << "Or " << (t-t2)/hour << " h" << endl;
cerr << "Or " << (t-t2)/minute<< " min" << endl;
//cerr << " You can't do the following:" << endl;
//Time t3 = v * v;
}catch(...){
cerr << "FATAL ERROR: unknown";
exit(1);
}
}
JP
Here's my final version, for now:
My test program is at the end
--------------------------
/// 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
#include <iostream>
#include <cmath>
#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.
Dev platform:
. Win2k -- gcc version 3.3.1 (cygming special)
Tested on:
. NT4.0 -- VC++ 6.0
. Win2k -- gcc version 3.2.3 (mingw special 20030504-1)
. Linus -- gcc version 3.2.2 20030222 (Red Hat Linux 3.2.2-5)
*/
template<int M,int L,int T, int K, int I>
class Units {
public:
Units(){
val=0;
}
/// Initialiser
Units(double v){
setValue(v);
}
/// Copy constructor
Units( const Units& u ) : val( u.getValue() ){}
/// Assignment operator
const Units& operator=( const Units& u ){ val=u.getValue(); return *this; }
// MULTIPLICATION AND DIVISION BY SCALARS
const Units& operator*=( double d ) { val*=d; return *this; }
const Units& operator/=( double d ) { val/=d; return *this; }
// ACCUMULATION AND DIMINUTION
Units& operator+=( const Units& u ) { val+=u.getValue(); return *this; }
Units& operator-=( const Units& u ) { val-=u.getValue(); return *this; }
/// Cast to scalar
/**
Not defined here because only Units<0,0,0,0,0> can cast to double
*/
operator double() const;
double getValue() const {
return val;
}
// Value checking
bool isValid(){
return !isnan(val) && !isinf(val);
}
bool isnan(){
return isnan(val);
}
bool isinf(){
return isinf(val);
}
private:
double val;
// used by */+- to make returning values easy
void setValue(double v){
val=v;
}
};
/// Casting to double
/**
Only defined for unitless types
*/
inline Units<0,0,0,0,0>::operator
return val;
}
// MULTIPLICATION
template<int M, int L, int T, int K, int I, int m, int l, int t, int k, int i>
inline
Units<M+m, L+l, T+t, K+k, I+i>
operator *(const Units<M,L,T,K,I> u,const Units<m,l,t,k,i> v){
return Units<M+m, L+l, T+t, K+k, I+i>(u.getValue() * v.getValue());
}
template<int m, int l, int t, int k, int i>
inline
Units<m, l, t, k, i>
operator *(double u,const Units<m,l,t,k,i> v){
return Units<m, l, t, k, i>(u * v.getValue());
}
template<int M, int L, int T, int K, int I>
inline
Units<M, L, T, K, I>
operator *(const Units<M,L,T,K,I> u,double v){
return Units<M, L, T, K, I>(u.getValue() * v);
}
template<int m, int l, int t, int k, int i>
inline
Units<m, l, t, k, i>
operator *(int u,const Units<m,l,t,k,i> v){
return Units<m, l, t, k, i>(u * v.getValue());
}
template<int M, int L, int T, int K, int I>
inline
Units<M, L, T, K, I>
operator *(const Units<M,L,T,K,I> u,int v){
return Units<M, L, T, K, I>(u.getValue() * v);
}
// DIVISION
template<int M, int L, int T, int K, int I, int m, int l, int t, int k, int i>
inline
Units<M-m, L-l, T-t, K-k, I-i>
operator/(const Units<M,L,T,K,I> u,const Units<m,l,t,k,i> v){
return Units<M-m, L-l, T-t, K-k, I-i>(u.getValue() / v.getValue());
}
template<int M, int L, int T, int K, int I>
inline
Units<M, L, T, K, I>
operator/(const Units<M,L,T,K,I> u,double v){
return Units<M, L, T, K, I>(u.getValue() / v);
}
template<int m, int l, int t, int k, int i>
inline
Units<-m, -l, -t, -k, -i>
operator/(double u,const Units<m,l,t,k,i> v){
return Units<-m, -l, -t, -k, -i>(u / v.getValue());
}
template<int m, int l, int t, int k, int i>
inline
Units<-m, -l, -t, -k, -i>
operator/(int u,const Units<m,l,t,k,i> v){
return Units<-m, -l, -t, -k, -i>(u / v.getValue());
}
template<int M, int L, int T, int K, int I>
inline
Units<M, L, T, K, I>
operator/(const Units<M,L,T,K,I> u,int v){
return Units<M, L, T, K, I>(u.getValue() / v);
}
// COMPARISON
template<int M, int L, int T, int K, int I>
inline
bool
operator==(const Units<M,L,T,K,I> u,const Units<M,L,T,K,I> v){
return u.getValue()==v.getValue()
}
template<int M, int L, int T, int K, int I>
inline
bool
operator!=(const Units<M,L,T,K,I> u,const Units<M,L,T,K,I> v){
return u.getValue()!=v.getValue()
}
template<int M, int L, int T, int K, int I>
inline
bool
operator<(const Units<M,L,T,K,I> u,const Units<M,L,T,K,I> v){
return u.getValue()<v.getValue();
}
template<int M, int L, int T, int K, int I>
inline
bool
operator<=(const Units<M,L,T,K,I> u,const Units<M,L,T,K,I> v){
return u.getValue()<=v.getValue()
}
template<int M, int L, int T, int K, int I>
inline
bool
operator>=(const Units<M,L,T,K,I> u,const Units<M,L,T,K,I> v){
return u.getValue()>=v.getValue()
}
// ADDITION AND SUBTRACTION
template<int M, int L, int T, int K, int I>
Units<M,L,T,K,I>
operator+(const Units<M,L,T,K,I> u,const Units<M,L,T,K,I> v){
return Units<M,L,T,K,I>(u.getValu
}
template<int M, int L, int T, int K, int I>
Units<M,L,T,K,I>
operator-(const Units<M,L,T,K,I> u,const Units<M,L,T,K,I> v){
return Units<M,L,T,K,I>(u.getValu
}
template<int M, int L, int T, int K, int I>
Units<M,L,T,K,I>
operator-(const Units<M,L,T,K,I> u){
return Units<M,L,T,K,I>(-u.getVal
}
// OUTPUT
template<int m, int l, int t, int k, int i>
inline
std::ostream& operator <<(std::ostream &os,const Units<m,l,t,k,i> &u){
os << u.getValue();
os.flags() & std::ios_base::showbase && os << " (dim)";
return os;
}
inline
std::ostream& operator <<(std::ostream &os,const Units<0,0,0,0,0> &u){
os << u.getValue();
return os;
}
#define DEFINE_OUTPUT_METHOD(MM,LL
inline \
std::ostream& operator <<(std::ostream &os,const Units<MM,LL,TT,KK,II> &u){ \
os << u.getValue(); \
os.flags() & std::ios_base::showbase && os << " " << UNITS; \
return os; \
}
DEFINE_OUTPUT_METHOD(0,1,0
DEFINE_OUTPUT_METHOD(0,0,1
DEFINE_OUTPUT_METHOD(0,1,-
DEFINE_OUTPUT_METHOD(1,0,0
DEFINE_OUTPUT_METHOD(0,0,0
//------------------------
// 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, -3, 0, 0, 0 > Density;
typedef Units<-1, 3, 0, 0, 0 > SpecificVolume;
typedef Units< 1, -1, -1, 0, 0> DynamicViscosity;
typedef Units< 1, 1, -3, -1, 0 > 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 Density;
typedef Units SpecificVolume;
typedef Units DynamicViscosity;
typedef Units Flowrate;
// Electrical
typedef Units Charge;
typedef Units ElecPotential;
typedef Units Capacitance;
typedef Units Resistance;
typedef Units Conductance;
#endif // CHECK_UNITS
//------------------------
// BASE UNITS FOR BASE MEASURES
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; // nice
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 Mass kg = kilogram;
const Length centimetre = metre / 100.0;
const Length kilometre = 1000.0 * metre;
const Area metre2 = metre*metre;
const Area hectare = (100.0*metre)*(100.0*metre
const Volume metre3 = metre2 * metre;
const Volume litre = milli * metre3;
const Volume centimetre3 = (centi*metre)*(centi*metre
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;
//------------------------
// HANDLING TEMPERATURE CONVERSIONS
//...
#endif // UNITS_H
--------------------------
#include "units.h"
#include <iostream>
#include <iomanip>
using namespace std;
int main(int argc, char *argv[]){
try{
cerr.flags(ios_base::showb
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;
Velocity vv = 10 * kilometre / hour;
cerr << "What about if the speed is " << vv << " faster?" << endl;
v+=vv;
cerr << "ie " << v << endl;
Time t2 = x / v;
cerr << "Then it takes " << t << " to travel " << x << endl;
cerr << "That's an improvements of " << t-t2 << endl;
cerr << "Or " << (t-t2)/hour << " h" << endl;
cerr << "Or " << (t-t2)/minute<< " min" << endl;
//cerr << " You can't do the following:" << endl;
//Time t3 = v * v;
}catch(...){
cerr << "FATAL ERROR: unknown";
exit(1);
}
}
>> how exactly it works when you take away those extra operators
You defined operator double() for Units<0, 0, 0, 0, 0> only, while i had to define it for all Units (unresolved externals). However, i got problems with extra operators that have a double as argument as these operators now are ambigous (e. g. for "metre * metre", the compiler could take any of the operators as any metre object could be turned to a double or not). Because of the constructor that takes a double, my compiler could turn any double, e. g. the r-value "metre * metre" to a Units object.
So, the main differences GCC / VC6 seem to be:
- GCC allows partial implementation of operators (operator double)
- VC6 takes a double for a Units object (and vice versa) and was able to deduce the
appropriate template parameters:
Area metre2 = metre * metre;
is equivalent to
Area metre2( (double)((double)metre) * (double)metre) );
Regards, Alex
You defined operator double() for Units<0, 0, 0, 0, 0> only, while i had to define it for all Units (unresolved externals). However, i got problems with extra operators that have a double as argument as these operators now are ambigous (e. g. for "metre * metre", the compiler could take any of the operators as any metre object could be turned to a double or not). Because of the constructor that takes a double, my compiler could turn any double, e. g. the r-value "metre * metre" to a Units object.
So, the main differences GCC / VC6 seem to be:
- GCC allows partial implementation of operators (operator double)
- VC6 takes a double for a Units object (and vice versa) and was able to deduce the
appropriate template parameters:
Area metre2 = metre * metre;
is equivalent to
Area metre2( (double)((double)metre) * (double)metre) );
Regards, Alex
ASKER
Hi Alex
Hmm that's a bit of a worry actually. If what you says is true then the class wouldn't be fulfilling its purpose in VC6, would it?
It needs to *only* allow cast to double in the unique case where the measurement is dimensionless. Otherwise, what's stopping you from adding metres to kilograms?
Does the commented-out section in the test prog throw a compilation error for you?
JP
Hmm that's a bit of a worry actually. If what you says is true then the class wouldn't be fulfilling its purpose in VC6, would it?
It needs to *only* allow cast to double in the unique case where the measurement is dimensionless. Otherwise, what's stopping you from adding metres to kilograms?
Does the commented-out section in the test prog throw a compilation error for you?
JP
Sorry, it seems that i doesn't work:
The following statement compiles and runs not throwing an exception:
Length z = metre + kilogram;
I tried to go back to my first compiling version (the first i posted above), however - for any reason - i couldn't get it compiled (e. g. cannot find appropriate constructor for Units<0, 1, -1, 0, 0>).
I removed all declarations/definitions of operator double() and it compiled .... beside of two exceptions:
Length z = metre + kilogram; // Isn't that great !!!
cerr << "The time taken to travel " << x << " at speed " << v << " is " << t << endl;
That can be repaired by
template<int M, int L, int T, int K, int I>
inline ostream& operator << (ostream& os, const Units<M, L, T, K, I>& u)
{ os << u.getval(); return os; }
Regards, Alex
The following statement compiles and runs not throwing an exception:
Length z = metre + kilogram;
I tried to go back to my first compiling version (the first i posted above), however - for any reason - i couldn't get it compiled (e. g. cannot find appropriate constructor for Units<0, 1, -1, 0, 0>).
I removed all declarations/definitions of operator double() and it compiled .... beside of two exceptions:
Length z = metre + kilogram; // Isn't that great !!!
cerr << "The time taken to travel " << x << " at speed " << v << " is " << t << endl;
That can be repaired by
template<int M, int L, int T, int K, int I>
inline ostream& operator << (ostream& os, const Units<M, L, T, K, I>& u)
{ os << u.getval(); return os; }
Regards, Alex
ASKER
Hey Alex
Send me your final version and I'll check it in GCC
Wish I could work out a nice way of doing unit-testing for compile-time error detection...
JP
Send me your final version and I'll check it in GCC
Wish I could work out a nice way of doing unit-testing for compile-time error detection...
JP
Hi JP,
i almost forgot your last request because i were at a different location from FRI to SUN.
Below is my latest running code on a VC6 Windows NT platform. The main difference to your final version is, that i omitted operator double() (as it would spoil all my multiplications and divisions) and that i used only member operators both for multiplication and division.
Regards, Alex
#include "units.h"
#include <iostream>
#include <sstream>
#include <string>
#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;
Length z = metre + metre; // metre + kilogram didn't compile
Units<0, 0, 0, 0, 0> unitless = 123.4; // that compiles
z = unitless * z; // seems to work fine
cerr << "The time taken to travel " << x << " at speed " << v << " is " << t << endl;
}catch(...){
cerr << "FATAL ERROR: unknown";
exit(1);
}
return 0;
}
//------------------------ ---------- ---------- --------
#ifndef UNITS_H
#define UNITS_H
#define CHECK_UNITS
#include <iostream>
using namespace std;
#ifdef CHECK_UNITS
// forward declaration !!!!
template<int M,int L,int T, int K, int I>
class 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; }
double getval() const { return 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.ge tval());
//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(u.getval());
*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;
}
*/
/// output overload
template<int M, int L, int T, int K, int I>
inline ostream& operator << (ostream& os, const Units<M, L, T, K, I>& u)
{ os << u.getval(); return os; }
/// 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 = (metre * 100.) * (metre * 100.);
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
// units.h
i almost forgot your last request because i were at a different location from FRI to SUN.
Below is my latest running code on a VC6 Windows NT platform. The main difference to your final version is, that i omitted operator double() (as it would spoil all my multiplications and divisions) and that i used only member operators both for multiplication and division.
Regards, Alex
#include "units.h"
#include <iostream>
#include <sstream>
#include <string>
#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;
Length z = metre + metre; // metre + kilogram didn't compile
Units<0, 0, 0, 0, 0> unitless = 123.4; // that compiles
z = unitless * z; // seems to work fine
cerr << "The time taken to travel " << x << " at speed " << v << " is " << t << endl;
}catch(...){
cerr << "FATAL ERROR: unknown";
exit(1);
}
return 0;
}
//------------------------
#ifndef UNITS_H
#define UNITS_H
#define CHECK_UNITS
#include <iostream>
using namespace std;
#ifdef CHECK_UNITS
// forward declaration !!!!
template<int M,int L,int T, int K, int I>
class 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; }
double getval() const { return 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
return Units<M+mm,L+ll,T+tt,K+kk,
//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(u.getval());
*reinterpret_cast<double*>
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
return val;
}
*/
/// output overload
template<int M, int L, int T, int K, int I>
inline ostream& operator << (ostream& os, const Units<M, L, T, K, I>& u)
{ os << u.getval(); return os; }
/// 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*>
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 = (metre * 100.) * (metre * 100.);
const Volume metre3 = metre2 * metre;
const Volume litre = milli * metre3;
const Volume centimetre3 = (centi*metre)*(centi*metre
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
// units.h
Centigrade + Farenheit isn't allowed either.... :-)