2017-04-20 83 views
0

我必須檢查某人的笨拙代碼,該代碼使用了20多個聯合,並根據數字方式將數據從字節數組中竄改並複製,以便通過模板實現這些方法(第二個字符)使用模板打字

template <class T> class type_punner 
{ 
    T& p; 
    unsigned char* pun; 

public: 
    type_punner(T& ref): p (ref), pun(reinterpret_cast<unsigned char*>(&p)) 
    { 
     static_assert(std::is_pod<T>::value, "type_punner can be used only for POD"); 
    } 


    inline unsigned char& at(std::size_t i) 
    { 
#ifdef QT_DEBUG 
     if(!(i < size())) throw std::out_of_range(__FUNCTION__); 
#endif 
#if (Q_BYTE_ORDER == Q_LITTLE_ENDIAN) 
     return pun[i]; 
#else 
     return pun[size() - i - 1]; 
#endif 
    } 

    inline unsigned char& reverse_at(std::size_t i) 
    { 
#ifdef QT_DEBUG 
     if(!(i < size())) throw std::out_of_range(__FUNCTION__); 
#endif 
#if (Q_BYTE_ORDER == Q_LITTLE_ENDIAN) 
     return pun[size() - i - 1]; 
#else 
     return pun[i]; 
#endif 
    } 

    // = 0 is LSB 
    inline unsigned char& operator[](std::size_t i) 
    { 
     return at(i); 
    } 

    inline std::size_t size() 
    { 
     return sizeof(T); 
    } 
}; 

只有我擔心,如果我保持標準兼容並通過返回的引用分配新值是有效的。

+0

那麼,它是在任何情況下未定義的行爲,根據標準去。這是你的工作,以確保你不會產生hydras。 – DeiDei

+0

@DeiDei我實際上並沒有生成九頭蛇,這不是同事創造九頭蛇的最壞例子。關於轉換爲字符類型指針等的規則發生了什麼?真的,沒有根據的評論是沒有幫助的,順便說一句,沒有辦法解決這個問題A)的目的是顯示數據並以反向或者直線的順序打包B)除了C++之外沒有其他的東西 – Swift

回答

1

您的代碼似乎合法。有幾點改進:

constexpr bool k_little_endian 
#if (Q_BYTE_ORDER == Q_LITTLE_ENDIAN) 
    = true; 
#else 
    = false; 
#endif 
constexpr bool k_debug 
#ifdef QT_DEBUG 
    = true; 
#else 
    = false; 
#endif 

template <class T, 
    bool little_endian = k_little_endian, 
    bool debug = k_debug 
> 
class type_punner { 
    T* p; 
    unsigned char* pun() const { return reintepret_cast<unsigned char*>(p); } 
public: 
    static_assert(std::is_pod<T>::value, "type_punner can be used only for POD"); 
    type_punner(T& ref): 
    p (std::addressof(ref)) 
    {} 
    type_punner(type_punner const&)=default; 
    type_punner()=delete; 

    unsigned char& at(std::size_t i) const noexcept(!debug) { 
    if (debug && !(i<size())) throw std::out_of_range(__FUNCTION__); 
    if (little_endian) 
     return pun()[i]; 
    else 
     return pun()[size() - i - 1]; 
    } 

    unsigned char& reverse_at(std::size_t i) const noexcept(!debug) { 
    if(debug && !(i < size())) throw std::out_of_range(__FUNCTION__); 
    if (little_endian) 
     return pun()[size() - i - 1]; 
    else 
     return pun()[i]; 
    } 
    // = 0 is LSB 
    unsigned char& operator[](std::size_t i) const noexcept(!debug) { 
    return at(i); 
    } 
    static constexpr std::size_t size() noexcept(true) { return sizeof(T); } 
}; 

首先,這將宏移開。你所看到的代碼往往容易推理,編譯器完全有能力消除死支。除非您想在您的類型上使用引用語義,否則很少會有想法存儲引用,並且在同一個類/結構中存儲引用旁邊的非引用幾乎不是一個好主意。

其次,內聯課堂是多餘的。

三,size既是constexpr也是static。

Forth,T&與默認複製/分配沒有任何合理的。 T*呢。所以我用T*

五,不需要存儲pun。每次生成零成本。

請注意,上面的type_punner可以在大端編碼環境中使用little_endian一個,或者只對有問題的部分進行調試。這是一個很小的編譯時間。

+0

哦,這很有趣參考字段!關於移動宏的好主意,'constexpr'不被支持,但'const'應該足夠了。我使用了宏定義來插入它,以防升級,而對於使用內聯的兩個編譯器中的一個不是重複的(它永遠不會擴展沒有內聯,或者擴展EVERY函數),並且不在類中存儲引用,static_assert()只能用於函數體,不支持默認或刪除方法。我當然會嘗試用生成的雙關語()而不是 – Swift

+0

什麼是noexcept(!debug)?從來沒有看到之前 – Swift

+1

@Swift它統計「這是保證不拋出,如果」調試「是錯誤的,但可能拋出,如果」調試「是真實的。」 – Yakk

0

只有我擔心,如果我保持標準兼容並通過返回的引用分配新值是有效的。

它是有效的,因爲那些引用是窄字符類型,這是特殊的。