2017-05-26 44 views
13

有兩種現有的類,一類是SrcField它返回混凝土類型值,而另一個是一個聯合DSTField,其限定相應的數據類型。如何簡化這個多餘的C++代碼?

class SrcField 
{ 
public: 
    signed char  GetInt8(); 
    unsigned char GetUInt8(); 
    short   GetInt16(); 
    unsigned short GetUInt16(); 
    int    GetInt32(); 
    unsigned int GetUInt32(); 
    float   GetFloat(); 
    double   GetDouble(); 
    bool   GetBool(); 
    DataType  GetType(); 
private: 
    DataType m_type; 
    DSTField m_data; 
}; 

union DSTField 
{ 
    signed char m_int8; 
    unsigned char m_uint8; 
    short   m_int16; 
    unsigned short m_uint16; 
    int   m_int32; 
    unsigned int m_uint32; 
    float   m_float; 
    double   m_double; 
    bool   m_bool; 
}; 

當我使用這兩個類,則應用程序是如以下。 這是非常多餘的;有沒有什麼好的方法來簡化它,比如模板,泛型編程等?

int main() 
{ 
    SrcField sf; 
    DSTField df; 

    switch(sf.GetType()) 
    { 
    case TYPE_INT8: 
     df.m_int8 = sf.GetInt8(); 
     break; 
    case TYPE_UINT8: 
     df.m_uint8 = sf.GetUInt8(); 
     break; 
    case TYPE_INT16: 
     df.m_int16 = sf.GetInt16(); 
     break; 
    case TYPE_UINT16: 
     df.m_uint16 = sf.GetUInt16(); 
     break; 
    case TYPE_INT32: 
     df.m_int32 = sf.GetInt32(); 
     break; 
    case TYPE_UINT32: 
     df.m_uint32 = sf.GetUInt32(); 
     break; 
    case TYPE_FLOAT: 
     df.m_float = sf.GetFloat(); 
     break; 
    case TYPE_DOUBLE: 
     df.m_double = sf.GetDouble(); 
     break; 
    case TYPE_BOOL: 
     df.m_bool = sf.GetBool(); 
     break; 
    default: 
     break; 
    } 
} 
+8

'boost :: variant'或'std :: variant'如何? –

+2

你爲什麼使用getters? – 2017-05-26 08:08:19

+0

..或'的boost :: any'(雖然我更喜歡'variant'太多,如果設定的種類受到限制。 – Nim

回答

7

使用std::variant你的代碼應該是這樣的:

#include <iostream> 
#include <variant> 

typedef std::variant< 
    signed char, 
    unsigned char, 
    short, 
    unsigned short, 
    int, 
    unsigned int, 
    float, 
    double, 
    bool 
> SrcField, DSTField; 

int main() 
{ 
    SrcField sf(97.0f); 
    DSTField df; 

    df = sf; 

    if(auto pval = std::get_if<float>(&df)) 
     std::cout << "variant value: " << *pval << '\n'; 
    else 
     std::cout << "failed to get value!" << '\n'; 
} 

注:由於這是C++ 17,對於以前的版本,我建議使用boost::variantboost::any或僅標頭執行Any類(例如我在我的項目中使用一個基於this

+2

'std :: variant'是新C++ 17標準的一部分。所以使用這些提升功能。 –

+0

感謝您的代碼。但是好像你已經重新定義了遺留類,事實上,這是第三部分導出類,我不能改變它。如果是這樣,是否意味着開關大小寫聲明無法避免? – freshyy

+0

@freshyy是的,我以後讀過你的評論。如果您無法更改定義,則僅限於執行API允許的內容。它似乎只爲你提供了類型的getter,所以我害怕在switch語句中使用它們是你所能做的。 – pergy

2

你說你不能通過ge SrcField,因此一個好的解決方案可能是使用訪問者。冗餘代碼仍然存在,但它只出現一次。看到這一點:

template<typename Visitor> 
constexpr void 
visitField(Visitor&& visitor, SrcField& field) 
{ 
    switch(field.GetType()) 
    { 
    case TYPE_INT8: 
     visitor(field.GetInt8()); 
     break; 
    case TYPE_UINT8: 
     visitor(field.GetUInt8()); 
     break; 
    .... 
    default: 
     throw std::runtime_error("invalid type"); 
} 

這樣您就能夠以簡單的方式來使用的值:

int main() 
{ 
    SrcField field; 
    visitField([](auto value) { 
     if constexpr(std::is_same<decltype(value), double>::value) 
      std::cout << "Hey, double here!\n"; 
     else if constexpr(std::is_same<decltype(value), bool>::value) 
      std::cout << "True or false?\n"; 
     else 
      std::cout << "Other types\n"; 
     std::cout << value << '\n'; 
    }, field); 
} 

在這種情況下我使用C++ 17的if constexpr能力。另一種可能使用lambda overload

你可以找到一個更完整的例子here on godbolt

注:正如你所看到的,我沒有使用DSTField可言。如果你確實需要使用DSTField,您可以使用類似的方法:

template<typename T> 
constexpr void 
setField(DSTField& dstField, T value) 
{ 
    static_assert(std::is_arithmetic<T>::value, 
        "value must be an arithmetic type"); 

    if constexpr(std::is_same<T, signed char>::value) 
     dstField.m_int8 = value; 
    else if constexpr(std::is_same<T, unsigned char>::value) 
     dstField.m_uint8 = value; 
    ... 
} 

可與一些用於像

DSTField dest; 
setField(dest, 4.f); 

其他說明:我標誌着visitField功能constexpr ,但我不確定你是否可以這樣使用。事實上,如果SrcField::GetType只能在運行執行,visitField將永遠不會在編譯時執行。

以外的其他注意事項:我不知道這可能取決於你的代碼或沒有,但你要記住,你不能確保signed charstd::int8_t(對於大多數其他類型,顯然)。如果您希望使代碼在外部架構上按預期工作,您應該使用固定寬度整數類型

+1

感謝您的詳細示例代碼和友好的建議。但我要解釋一下我的使用環境: 1.我必須從SrcField值分配給DSTField領域,這是一個強制性的對象作爲參數傳遞給下一個函數調用。 2.由於#1,正如你所建議的,我必須編寫代碼* setField *和* visitField *。這可能會變得有點矯枉過正,因爲實際上,我只寫了一次可執行的任務,所以源代碼的數量看起來不亞於我的。 在我的第一次測試中,我使用宏來簡化,但它不容易在未來trobuleshooting。 – freshyy

+2

@freshyy的確如此:如果你只需要做這個__once__,那麼唯一能夠幫助你的兩件事情就是宏,也許在將來會有所反思。正如你所說,最好小心宏;) – dodomorandi