带有Scale的C++维度分析(Barnes和Nackman)

Jer*_*unh 6 c++ design-patterns

我最近正在阅读关于C++ Source的系列文章,"暂停反思:五个五个列表".在第五部分中,Scott Meyers讨论了Barton和Nackman解决单位问题的方法.作为航空航天业的嵌入式软件工程师,这个特别的啊哈!片刻让我很兴奋.到目前为止,我还没有听说过这种方法(也没有这些作者).

我已经做过研究,试图找到有关该解决方案的更多信息.我在这里看到了这个演讲:http://se.ethz.ch/~meyer/publications/OTHERS/scott_meyers/dimensions.pdf

我想我理解了我在这个解决方案上所阅读的所有内容.但我觉得有一块拼图丢失了.这个美丽,优雅的解决方案无处可扩展.具体来说,我对转换不仅仅是一个倍增因素感兴趣.例如,温度在开尔文,摄氏和华氏之间转换.我希望能够互换使用这些温度.

我的问题:

  1. 我错过了什么?是否参考了我忽略的单位解决方案讨论,在某处讨论了规模?

  2. 如果没有,我怎么能进一步解决这个问题?是否存在可与B&N方法结合使用以完成解决方案的现有模式?

我的目标是能够在没有过多计算的情况下使用类似下面示例的代码.在距离的情况下,我希望能够声明一个定义为英里的对象,并以英里为单位执行所有相关计算,而不必经常来回转换为米.

例:

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
Run Code Online (Sandbox Code Playgroud)

注意:我已经看到了BOOST UNITS解决问题的方法,我不喜欢它.对我来说这是非常难以理解的.我也不是,通常允许使用外部库,如boost.

资料备份:

单位类如下所述:

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());
    }
Run Code Online (Sandbox Code Playgroud)

这个类允许我们编写如下代码:

//  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;
Run Code Online (Sandbox Code Playgroud)

mst*_*aum 0

根据我对您的代码和解释的理解,您似乎可以定义转换常量“ Units”,例如

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;
Run Code Online (Sandbox Code Playgroud)

上面的代码与重载运算符一起使用,同时保持模板化单位一致。您必须Unit为要使用的任何常量创建伪变量。

我可以看到的另一种工作方式是编写一个类似的函数

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
     }
}
Run Code Online (Sandbox Code Playgroud)