mardi 24 février 2015

C++ Dimensional Analysis (Barnes and Nackman) with Scale

I was recently reading the series on the C++ Source, “A Pause to Reflect: Five Lists of Five”. In Part V, Scott Meyers discusses the Barton and Nackman solution to the units problem. As an embedded software engineer for the aerospace industry, this particular Aha! Moment got me excited. Up until this point I’ve not heard of this approach (nor these authors).


I have done research trying to find more information regarding the solution. I came across this presentation here: http://ift.tt/18hs4rz


I think I understand everything that I have read on this solution. But I feel like there is a piece of the puzzle missing. Nowhere does this beautiful, elegant solution address scale. Specifically I am interested in conversions that are more than just a multiplication factor. For example, Temperature converting between Kelvin, Celsius, and Fahrenheit. I would like to be able to use these temperatures interchangeably.


My Questions:




  1. Did I miss something? Is scale discussed somewhere in reference to the Units solution discussion that I have overlooked?




  2. If not, How could I approach this problem further? Is there an existing pattern that could be used in combination with the B&N approach to complete the solution?




Note: I have seen the BOOST UNITS solution to the problem and I don’t like it. To me it is very unreadable. Nor am I, typically allowed to use external libraries such as boost.


Backup Data:


The Units Class as described:



template<class T, // Precision
int m, // Mass
int l, // Length
int t, // Time
int q, // Charge
int k, // Temperature
int i, // Luminous Intensity
int a> // Angle

class Units
{
public:
// ------------------------------------------------------
explicit
Units (T initVal = 0)
: val (initVal)
{
}

// --------------------------------------------------------------------
// Operator: Assignment from type T
Units<T, m, l, t, q, k, i, a>&
operator= (const T rhs)
{
val = rhs;
return *this;
}

// --------------------------------------------------------------------
// Operator: Type Converstion to T
operator T () const
{
return val;
}

// --------------------------------------------------------------------
// Operator: +=
Units<T, m, l, t, q, k, i, a>&
operator+= (const Units<T, m, l, t, q, k, i, a>& rhs)
{
val += rhs.val;
return *this;
}

// --------------------------------------------------------------------
Units<T, m, l, t, q, k, i, a>&
operator-= (const Units<T, m, l, t, q, k, i, a>& rhs)
{
val -= rhs.val;
return *this;
}

// --------------------------------------------------------------------
Units<T, m, l, t, q, k, i, a>&
operator*= (T rhs)
{
val *= rhs;
return *this;
}

// --------------------------------------------------------------------
Units<T, m, l, t, q, k, i, a>&
operator/= (T rhs)
{
val /= rhs;
return *this;
}

// --------------------------------------------------------------------
// Get Reference
T&
Val ()
{
return val;
}

// --------------------------------------------------------------------
// Get Value
const T&
Val () const
{
return val;
}

private:
T val;
};

// ----------------------------------------------------------------------------
// Operator: Addition
template<class T, int m, int d, int t, int q, int k, int i, int a>
const Units<T, m, d, t, q, k, i, a>
operator+ (const Units<T, m, d, t, q, k, i, a> & lhs,
const Units<T, m, d, t, q, k, i, a> & rhs)
{
Units<T, m, d, t, q, k, i, a> result (lhs);
return result += rhs;
}

// ----------------------------------------------------------------------------
// Operator: Subtraction
template<class T, int m, int d, int t, int q, int k, int i, int a>
const Units<T, m, d, t, q, k, i, a>
operator- (const Units<T, m, d, t, q, k, i, a> & lhs,
const Units<T, m, d, t, q, k, i, a> & rhs)
{
Units<T, m, d, t, q, k, i, a> result (lhs);
return result -= rhs;
}

// ----------------------------------------------------------------------------
// Operator: Multiplication
template<class T, int m, int d, int t, int q, int k, int i, int a>
const Units<T, m, d, t, q, k, i, a>
operator* (const Units<T, m, d, t, q, k, i, a> & lhs,
const Units<T, m, d, t, q, k, i, a> & rhs)
{
Units<T, m, d, t, q, k, i, a> result (lhs);
return result *= rhs;
}

// ----------------------------------------------------------------------------
// Operator: Division
template<class T, int m, int d, int t, int q, int k, int i, int a>
const Units<T, m, d, t, q, k, i, a>
operator/ (const Units<T, m, d, t, q, k, i, a> & lhs,
const Units<T, m, d, t, q, k, i, a> & rhs)
{
Units<T, m, d, t, q, k, i, a> result (lhs);
return result /= rhs;
}

// ----------------------------------------------------------------------------
// Operator: Multiplication (Creates New Type)
template<class T,
int m1, int d1, int t1, int q1, int k1, int i1, int a1,
int m2, int d2, int t2, int q2, int k2, int i2, int a2>

// Return Type
Units<T, m1 + m2, d1 + d2, t1 + t2, q1 + q2, k1 + k2, i1 + i2, a1 + a2>
operator* (const Units<T, m1, d1, t1, q1, k1, i1, a1>& lhs,
const Units<T, m2, d2, t2, q2, k2, i2, a2>& rhs)
{
// New Return type
typedef Units<T,
m1 + m2,
d1 + d2,
t1 + t2,
q1 + q2,
k1 + k2,
i1 + i2,
a1 + a2> ResultType;

return ResultType (lhs.Val() * rhs.Val());
}

// ----------------------------------------------------------------------------
// Operator: Division (Creates New Type)
template<class T,
int m1, int d1, int t1, int q1, int k1, int i1, int a1,
int m2, int d2, int t2, int q2, int k2, int i2, int a2>

// Return Type
Units<T, m1 - m2, d1 - d2, t1 - t2, q1 - q2, k1 - k2, i1 - i2, a1 - a2>
operator/ (const Units<T, m1, d1, t1, q1, k1, i1, a1>& lhs,
const Units<T, m2, d2, t2, q2, k2, i2, a2>& rhs)
{
// New Return type
typedef Units<
T,
m1 - m2,
d1 - d2,
t1 - t2,
q1 - q2,
k1 - k2,
i1 - i2,
a1 - a2> ResultType;

return ResultType (lhs.Val() / rhs.Val());
}


This class allows us to write code that looks like this:



// Base Types
typedef Units<double, 1,0,0,0,0,0,0> uMass;
typedef Units<double, 0,1,0,0,0,0,0> uLength;
typedef Units<double, 0,0,1,0,0,0,0> uTime;
typedef Units<double, 0,0,0,1,0,0,0> uCharge;
typedef Units<double, 0,0,0,0,1,0,0> uTemperature;
typedef Units<double, 0,0,0,0,0,1,0> uIntensity;
typedef Units<double, 0,0,0,0,0,0,1> uAngle;

// Derived Types
typedef Units<double, 0,2, 0,0,0,0,0> uArea;
typedef Units<double, 0,3, 0,0,0,0,0> uVolume;
typedef Units<double, 0,1,-1,0,0,0,0> uVelocity;
typedef Units<double, 0,1,-2,0,0,0,0> uAcceleration;
typedef Units<double, 1,1,-2,0,0,0,0> uForce;


uMass mass;
uTime time;
uForce force;
uLength length;
uVelocity velocity;
uAcceleration acceleration;

// This will compile
mass = 7.2;
acceleration = 3.5;
force = mass * acceleration;

// These will not compile ** Enforcing Dimensional Unit Correctness
force = 7.2 * acceleration;
force = mass;
force *= acceleration;

Aucun commentaire:

Enregistrer un commentaire