2017-05-26 24 views
13

Varolan iki sınıf vardır; biri, beton türü değerini döndüren SrcField ve diğeri, karşılık gelen veri türünü tanımlayan bir DSTField birleşimidir.Bu gereksiz C++ kodunu nasıl basitleştirebilirim?

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; 
}; 

Her iki sınıfı da kullandığımda, uygulama aşağıdaki gibidir. Çok gereksiz; şablonları, jenerik programlama, vb. gibi basitleştirmek için iyi bir yol var mı? şu şekilde görünecektir std::variant kodunuzu kullanarak

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

Nasıl "varyant" veya "std :: variant" desteği hakkında? –

+2

Neden getters kullanıyorsunuz? –

+0

..ya da "boost :: any" (türlerin kısıtlı olması durumunda bile varyantı tercih ederim.) – Nim

cevap

7

:

#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'; 
} 

Not: Ben boost::variant, boost::any kullanmak tavsiye önceki sürümleri için, ++ 17 c oluyor ya yana bir başlık okunur Any sınıfının uygulanması (örneğin projemde this'u temel alan birini kullanıyorum)

+2

'std :: variant', yeni C++ 17 standardının bir parçasıdır. Yani bu yetenekleri kullan. –

+0

Kodunuz için teşekkürler. Ama eski sınıfını yeniden tanımlamışsınız gibi görünüyor, aslında, 3. sınıf ihraç edilen sınıf, bunu değiştiremiyorum. Eğer öyleyse, anahtar-durum bildirimi önlenemez mi? – freshyy

+0

@freshyy Evet, daha sonra bunun hakkında yorumunuzu okudum. Tanımları değiştiremezseniz, API'nin izin verdiği şeyi yapmakla sınırlandırılmıştır. Bu size sadece tür alıcılar sağlamak gibi görünüyor, bu yüzden bir anahtar deyim kullanarak bunları kullanmaktan korkuyorum. – pergy

2

Değişemeyeceğinizi söylemiştiniz Bu nedenle, iyi bir çözüm, bir ziyaretçinin kullanılması olabilir. Yedek kod hala var, ancak sadece bir kez var. Bu bakınız:

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"); 
} 

basit bir şekilde değerleri kullanmak mümkün Bu şekilde:

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'den if constexpr yeteneği kullanılan Bu durumda. Bir başka olasılık bir lambda overload

Sen Daha kapsamlı bir örnek here on godbolt

Not bulabilirsiniz kullanın: Gördüğünüz gibi, ben hiçDSTField kullanmıyordu. Gerçekten DSTField kullanmak gerekiyorsa, benzer bir yaklaşım kullanabilirsiniz: böyle bir şeyle kullanılabilir

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); 

Diğer not: Ben visitField fonksiyon olarak constexpr işaretlenmiş ama bu şekilde kullanabileceğinden emin olamam. Gerçekten de,sadece çalışma zamanında, visitField adresinde çalıştırılabiliyorsa derleme zamanında hiçbir zaman çalıştırılmayacaktır.

Diğer diğer not:signed char olarak çoğu için (bir std::int8_t olduğunu bu kodu veya değil bağlı olabilir eğer bilmiyorum, ama sen emin olamaz olabilir akılda tutmak zorunda diğer türlerde, açıkça). Kodunuzun yabancı mimarlarda beklendiği gibi çalışmasını istiyorsanız, sabit genişlikli tamsayı türleri'u kullanmalısınız.

+1

Detaylı örnek kodlarınız ve nazik önerileriniz için teşekkür ederiz. Ama kullanım bağlamımı açıklamam gerekiyor: 1. Bir sonraki işlev çağrısına parametre iletimi olarak zorunlu bir nesne olan DSTField alanına atamak için SrcField öğesinden alması GEREKİR. 2. # 1'den dolayı, önerdiğiniz gibi * setField * ve * visitField * kodlarını yazmam gerekiyor. Bu biraz aşırı olabilirdi, çünkü aslında, bir kez bir kez almak için bir görev yazmak, böylece kaynak kodları miktarı benimkinden daha az görünmüyor. İlk testimde , sadeleştirmek için makroyu kullandım, ancak ileride sıkıntı vermek kolay değil. – freshyy

+2

@freshyy Gerçek: Eğer sadece bu __once__ yapman gerekiyorsa, size yardımcı olabilecek tek iki şey, makrolar ve belki de gelecekte, yansıma. Ve dediğiniz gibi, makrolara dikkat etmek daha iyidir;) – dodomorandi

İlgili konular