C++ is uniquely suited to tackle the problems posed by Dimenso without need for
code generation. The concept of interrelated types that refer to one another can be represented
entirely by a template, parameterized by the dimension exponents. There is no limit to the number
of dimensions that can be represented, since only template instantiations that are used in the project
will be found in the executable.
Using DimensionedValue looks and feels close to the habitual use of units and dimensions.
length length1=10*inch;
length length2=3*centi*meter;
mass m=10*kilogram; // "kilogram" is optional
acceleration g=9.81; // same as 9.81*meter/(second*second)
force f=m*g;
Outputting and conversion to specific units is very easy.
cout << length1 << endl;
cout << length1 / feet << " in feet" << endl;
The code is available as a
C++ header file.
The main class is
DimensionedValue. Notice the straightforward way that
dimensions for the resulting type of each operation are calculated.
template ;
class DimensionedValue {
double v;
public:
DimensionedValue(double val) : v(val) {}
inline double magnitude() { return v; }
template
DimensionedValue
operator*(DimensionedValue other) {
return DimensionedValue(
magnitude()*other.magnitude()
);
}
template
DimensionedValue
operator/(DimensionedValue other) {
return DimensionedValue(
magnitude()/other.magnitude()
);
}
DimensionedValue operator+(DimensionedValue other) {
return DimensionedValue(magnitude()+other.magnitude());
}
DimensionedValue operator-(DimensionedValue other) {
return DimensionedValue(magnitude()-other.magnitude());
}
void printOn(ostream& os) {
os<::printOn(os);
}
bool unitsAreDerived() {
return DimensionedUnit::unitsAreDerived();
}
static const int mExponent;
static const int kExponent;
static const int sExponent;
static const int aExponent;
};
template
const int DimensionedValue::mExponent = L;
template
const int DimensionedValue::kExponent = M;
template
const int DimensionedValue::sExponent = T;
template
const int DimensionedValue::aExponent = C;
template
static ostream& operator<<(
ostream& os,
DimensionedValue u) {
u.printOn(os);
return os;
}
template
static DimensionedValue
operator*(double d,DimensionedValue u) {
return DimensionedValue(d*u.magnitude());
}
DimensionedUnit is a helper class that outputs the string representation
of the units.
template
class DimensionedUnit {
static bool printUnit(
bool showDot,
ostream& os,
char* unit,
int dim) {
if(dim!=0) {
if(showDot) {
os<<'\xb7';
}
os<
It uses template specialization to override the default behavior for standard units.
template<>
struct DimensionedUnit<0,0,-1,0> {
static void printOn(ostream& os) {
os<<"Hz";
}
static bool unitsAreDerived() { return true; }
};
. . .
The standard types are defined as typedefs. A special
case was made for "time" since it conflicted with a function
defined in "time.h". The type was named "time_".
typedef DimensionedValue<0,0,0,0> unity;
typedef DimensionedValue<1,0,0,0> length;
typedef DimensionedValue<0,1,0,0> mass;
. . .
typedef DimensionedValue<0,0,1,1> charge;
. . .
The standard units are defined as values. When multiplied by numbers,
they yield dimensioned values of the appropriate dimension.
const length meter=1;
const length inch=0.0254;
const length foot=12*inch;
const length feet=foot;
const length yard=3*feet;
const length mile=5280*feet;
. . .
For convenience, the standard SI qualifiers are also defined as values.
They can be multiplied by units to yield "qualified units", e.g. kilo*newton.
const double yotta=1e24;
const double zetta=1e21;
const double exa=1e18;
. . .