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

I recently read a series in C ++ Source, "A Pause to Reflect: Five Lists of Five". In Part V, Scott Meyers discusses the solution to Barton and Nackman with the unit problem. Being a software engineer for the aerospace industry, this Aha! The moment turned me on. Until now, Ive not heard of this approach (and these authors).

I did a study trying to find more information about the solution. I came across this presentation here: http://se.ethz.ch/~meyer/publications/OTHERS/scott_meyers/dimensions.pdf

I think I understand everything that I read about this decision. But I feel that part of the puzzle is missing. Nowhere is this beautiful, elegant solution suitable. In particular, I am interested in transformations that are more than just a multiplication factor. For example, temperature conversion between Kelvin, Celsus and Fahrenheit. I would like to be able to use these temperatures interchangeably.

My questions:

  • Did I miss something? Is the scale being discussed somewhere in relation to the discussion of the Units solution that I missed?

  • If not, how can I approach this problem further? Is there an existing model that can be used in conjunction with the B & N approach to complete the solution?

My goal is to have code that looks like the example below without excessive computation. In the case of distance, I want to be able to declare an object defined as a mile and do all my corresponding calculations as miles, without having to constantly convert back and forth to counters.

Example:

typedef Units<double, miles> uMiles; typedef Units<double, kilometers> uKilometers; uMiles d1 (1.0); uKilometers d2 (1.60934); d1 += d2; if (d1.val(miles) == 2.0) // PASS if (d1.val(kilometers) == 3.21869) // PASS 

Note: I saw a solution to the BOOST UNITS problem, and I don't like it. For me it is very impenetrable. I am also not allowed to use external libraries such as boost.

Backup Data:

Unit 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; 
+6
source share
2 answers

From what I understand from your code and your explanation, it would seem that you could define the conversion constant " Unit s", such as

 Units<double,0,0,0,0,0,0,0> K2C(243.15); Units<double,0,0,0,0,1,0,0> uCelsius; Units<double,0,0,0,0,1,0,0> uKelvin; uCelsius = uKelvin - K2C; 

The code above works with an overloaded operator, while maintaining consistent modules. You will need to create a pseudo- Unit for any constants that you want to use.

Another way I could work would be to write a function like

 typdef enum { CELSIUS, KELVIN, FAHRENHEIT } temp_t; void Units::convertTemp(const temp_t from, const temp_t to) { switch(from) { case KELVIN: val -= 243.15; case CELSIUS: if(to == FAHRENHEIT) //conversion else if(to == KELVIN) val += 243.15; break; case FAHRENHEIT: // convert to celsius if(to == KELVIN) //convert to Kelvin } } 
0
source

In physics, the value that a unit has, such as speed, when multiplied by a scalar, retains its unit. This means that you are saying something like:

 1.6 * 7 kilometers per hour = 11.2 kilometers per hour 

does not change units. What we really wanted to do was convert kilometers per hour to miles per hour, but instead we just multiplied kilometers per hour by a factor. Just because the amount you get is equivalent to miles per hour, does not mean that you actually represent miles per hour.

In defining your template, you always allow one view of each of the basic units .

So far, you have differentiated between different types of physical quantities, but not between different types of unit systems. In the above code, your device / type is Speed , not milesPerHour , and you still manually remember which actual unit you are using and translate it between SI units manually (without using a type system). Effectively, if you had one variable initialized at miles per hour and another at kilometers per hour, would they be of one type or of another type? I will argue that they must be of different types.

To make all units (in different measurement systems) different types, we could write one Units template for the SI unit system and equivalent for the Imperial system unit .

 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 SIUnits { // ... 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 ImperialUnits { // ... 

Now we need to find a good way to let our type system automatically map us between two system systems.

Below is one possible (but terrible) solution:

 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 ImperialUnits<T, m, l, t, q, k, i, a> convert(SIUnits<T, m, l, t, q, k, i, a> value) { T conversionFactor = 1.0; for (int x = 0; x < m; ++x) { // This is some function that maps from one to the other. conversionFactor *= siMassToImperialMassFactor; conversionFactor += siMassToImperialMassOffset; } for (int x = m; x < 0; ++x) { // This is some function that maps from one to the other. conversionFactor *= siMassToImperialMassFactor; conversionFactor += siMassToImperialMassOffset; } // Do the same for other dimensions as well... 

The problem you see with Kelvin, Fahrenheit, and Celsius is that you are not changing the basic measurement system, which will require you to create a new type, but simply remember the violin manually, a factor that you need to pretend to convert between the two systems.

In essence, if we removed the templates and used pure classes, we would have something like:

 class Celsius; class Kelvin; class Fahrenheit { // ... Fahrenheit(Celsius t); // Auto-convert from celsius Fahrenheit(Kelvin t); // Auto-convert from Kelvin // ... 

These are different types, so we can use a type system to determine conversions between them.

Finally, I can explain what I actually offer. In your Units template, you have a base type and identifiers for each of the physical measurements, and one of the solutions would be to add another element to indicate the measurement system used, i.e.

 template<class T, // Precision int SystemOfUnits, // Denotes the system of units used, SI or Imperial. int m, // Mass int l, // Length int t, // Time int q, // Charge int k, // Temperature int i, // Luminous Intensity int a> // Angle class Units { // etc. 

However, I do not like the above solution, since the value of the SystemOfUnits template SystemOfUnits encodes too much information about your template. I would probably be more explicit in my definition, which precisely indicates the units that my measurement system uses, i.e.

 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 M, // Mass unit type class L, // Length unit type class T, // Time unit type class Q, // Charge unit type class K, // Temperature unit type class I, // Luminous Intensity unit type class A> // Angle unit type class Units { // etc. 

This would force you to use a consistent measurement system for dimensional analysis by doubling the arguments of your template.

0
source

Source: https://habr.com/ru/post/982948/


All Articles