2015-02-24 51 views
6

我最近正在閱讀C++源代碼,「一個反映的停頓:五個列表五」的系列。在Part V中,Scott Meyers討論了Barton和Nackman解決單位問題的方法。作爲航空航天業的嵌入式軟件工程師,這個特別的Aha!時刻讓我興奮。到目前爲止,我還沒有聽說過這種方法(也沒有這些作者)。C++維度分析(巴恩斯和Nackman)與規模

我已經做了研究,試圖找到更多關於解決方案的信息。我在這裏遇到了這個演示文稿:http://se.ethz.ch/~meyer/publications/OTHERS/scott_meyers/dimensions.pdf

我想我理解了我在這個解決方案中讀到的所有內容。但我覺得缺少一塊難題。沒有這個美麗,優雅的解決方案地址的規模。具體而言,我對轉換不僅僅是一個倍增因素感興趣。例如,Kelvin,Celsius和Fahrenheit之間的溫度轉換。我希望能夠交替使用這些溫度。

我的問題:

  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 

注: 我所見過的壓單元解決的問題,我不喜歡它。對我來說這是非常不可讀的。我也不是,通常被允許使用外部庫如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()); 
    } 

這個類允許我們編寫的代碼看起來像這樣:

// 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; 
+0

我確實想要說明,我已經購買了Scott Meyers通過Barton和Nackman提及的那本書。我希望這本書能夠提供更多的見解。 http://www.amazon.com/Scientific-Engineering-Introduction-Advanced-Techniques/dp/0201533936/ref=sr_1_1?ie=UTF8&qid=1424790308&sr=8-1&keywords=Barton+Nackman – Jerunh 2015-02-24 15:05:42

+1

單位是可以乘法組合的東西。度量標度不是(因爲它們有一個任意的零點,並不意味着「無」),所以我不希望有一個「單位」庫來處理它們。 – 2015-02-24 15:33:03

+0

@MikeSeymour我不同意。所有單位都有隱含的規模。我只是尋找一種明確的方式,並提供一種轉換方式。提升單位也提供規模,雖然以一種醜陋的方式。 – Jerunh 2015-02-24 15:59:56

回答

0

從我的理解你的代碼和你的解釋,似乎你可以定義轉換常數「Unit s」,如

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; 

上面的代碼可以在保持模板單元一致的同時使用重載操作符。你將不得不爲你想使用的任何常量創建pseudo- 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 
    } 
} 
+0

這並不符合我對交換使用單位的期望。您提供的兩種解決方案都需要明確的轉換 – Jerunh 2015-02-24 18:35:36

+0

我已更新我的問題以顯示所需代碼的示例。 – Jerunh 2015-02-24 18:36:53

0

在物理學中,有一個單元的值,如速度,當一個標量乘以保留其單位。這意味着,說是這樣的:

1.6 * 7 kilometers per hour = 11.2 kilometers per hour 

不改變單位。我們實際上想要做的是將每小時的公里數轉換爲每小時的數英里數,但是我們只是將每小時的公里數乘以一個因數。僅僅因爲你得到的數字相當於每小時的里程數並不意味着你實際上代表了每小時的里程數。

在您的模板定義中,您只允許每種基本單元的一種

到目前爲止,您已經區分不同種類的物理量,但不是在不同種類的單位系統之間進行區分。在上面的代碼中,您的單位/類型爲Speed,而不是milesPerHour,並且您仍然手動記住您正在使用的實際單位,並用手(不使用類型系統)在它與SI單位之間進行轉換。實際上,如果您將一個變量初始化爲每小時英里數,另一個變量爲每小時千米數,那麼它們是否具有相同類型或不同類型?我會爭辯說他們應該是不同的類型。

使所有的單位的(在不同的測量系統)不同類型,我們可以寫爲SI units system一個Units模板,具有同等的一個用於Imperial units system

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

現在,我們需要想出一個好辦法,讓我們的類型系統兩個單位系統之間自動映射我們。

下面是一個可能的(但可怕的)解決方案:

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... 

,你與開爾文,華氏,攝氏看到的問題是事實,你並沒有改變做底層測量系統,這將需要您製作一個新的類型,但僅僅是手動記住假裝在兩個系統之間進行轉換所需的小提琴因素。

從本質上講,如果我們去掉了模板和使用純類,我們會碰到這樣的:

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

這些是不同類型的,這樣我們就可以使用類型系統來定義它們之間的轉換。

最後,我可以解釋我實際上提出的。在你Units模板,你必須爲每個物理尺寸和一個解決方案的基本類型和標識符將增加一個項目來表示所使用的測量系統,即

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. 

然而,我不喜歡上述解決方案,因爲SystemOfUnits模板參數的值會對您的模板的太多信息進行編碼。我可能會在我的定義更明確指出到底是什麼單位我測量的系統使用,即

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. 

這將迫使你加倍你的模板參數的使用成本計量的一致的系統維度分析。