2009-10-01 36 views
1

我有一些struct containig位域,它可能會有所不同。例如:具有混疊的可變大小的位域

struct BitfieldSmallBase { 
    uint8_t a:2; 
    uint8_t b:3; 
    .... 
} 

struct BitfieldLargeBase { 
    uint8_t a:4; 
    uint8_t b:5; 
    .... 
} 

工會在一次訪問所有位:

template<typename T> 
union Bitfield 
{ 
    T bits; 
    uint8_t all; // <------------- Here is the problem 

    bool operator & (Bitfield<T> x) const { 
     return !!(all & x.all); 
    } 
    Bitfield<T> operator + (Bitfield<T> x) const { 
     Bitfield<T> temp; 
     temp.all = all + x.all; //works, because I can assume no overflow will happen 
     return temp; 
    } 
    .... 
} 

typedef Bitfield<BitfieldSmallBase> BitfieldSmall; 
typedef Bitfield<BitfieldLargeBase> BitfieldLarge; 

的問題是:對於一些位域的基類,一個uint8_t是不夠的。 BitfieldSmall確實適合uint8_t,但是BitfieldLarge不適用。數據需要儘可能緊湊地包裝(稍後將由SSE指令處理),因此始終使用uint16_t是無可爭議的。有沒有辦法用整數類型聲明「全部」字段,其大小與位域相同?或者另一種方式來訪問整體?

我當然可以放棄模板的使用並明確聲明每種位域,但是我想避免代碼重複(這裏有相當多的操作符和成員函數)。

回答

5

您也可以使整型類型成爲模板參數。

template<typename T, typename U> 
union Bitfield 
{ 
    T bits; 
    U all; 
} 

typedef Bitfield<BitfieldSmallBase, uint8_t> BitfieldSmall; 
typedef Bitfield<BitfieldLargeBase, uint16_t> BitfieldLarge; 
+0

有時溶液是如此簡單:-) – hirschhornsalz 2009-10-01 12:06:40

1

我學到了艱辛的道路,雖然在您使用瓦爾位寬爲讓編譯器做你的遮蔽,併爲您的控制換檔的便捷方式,您不能做出的假設結構中成員的順序和填充。它依賴於編譯器,編譯器確實會改變順序,並且依賴於項目中的其他代碼。

如果你想把一個字節看作離散的字段,你真的必須這麼做。

+0

可移植性是次要的,因爲我需要仍要使用SSE,所以這是僅限於x86_64的(以及可能的x86)。對於我正在使用的編譯器(gcc,icc),我可以對結構的佈局作出安全的假設,它在ABI中進行了定義。 – hirschhornsalz 2009-10-01 12:31:00

+0

但在兩個編譯器中不保證是相同的。或者在這些編譯器的* next *版本中。 – jalf 2009-10-01 12:33:19

+0

icc和gcc都符合x86_64 ABI。不時有ABI斷裂,但他們通常會盡力減少影響。如果您發現一個案例,icc的結構佈局與gcc不同,您甚至可能會提交一個錯誤,因爲icc難以儘可能兼容gcc。 – hirschhornsalz 2009-10-01 12:58:56

1

你可以用模板元編程來定義,從BitfieldSmallBase,BitfieldLargeBase等映射到另一種類型的模板函數 - 默認uint8_t和BitfieldLargeBase到uint16_t爲模板專業化,然後使用這樣的:

union Bitfield 
{ 
    T bits; 
    typename F<T>::holder_type all; 
}; 
+0

另一個不錯的解決方案。但我更喜歡John Kugelmans的解決方案,它更簡單。 – hirschhornsalz 2009-10-01 12:16:02

+0

如果BitfieldSmall *類很少,John有更好的解決方案;如果他們中有很多人,我的情況會更好,但是有一兩個人可以脫穎而出,比如uint16_t和默認的uint8_t。 – catwalk 2009-10-01 12:30:54

+0

有3個位域類,其中一個是雙字節大小的(到目前爲止)。因爲F :: holder_type及其專業化的聲明超過3行,Johns解決方案在這種情況下效果更好,儘管幅度非常小;-) – hirschhornsalz 2009-10-01 12:36:13

1

您可能要考慮std::bitsetboost::dynamic_bitset而不是滾動自己的。無論如何,避開std::vector<bool>

+0

感謝您的警告;-)我在此部分中使用的數組該程序的大小是非常固定的(與底層數據結構相反),所以我儘量避免使用std :: vector。 boost並不會簡化事情,因爲我需要通過SSE(__m128i等)訪問數據來完成實際的工作。 – hirschhornsalz 2009-10-01 12:23:20

0

製作的字節數,你所需要的模板參數的一部分:

template <typename T, int S=1> 
struct BitField 
{ 
    union 
    { 
     T bits; 
     unsigned char bytes[S]; 
    }; 
}; 

typedef Bitfield<BitfieldSmallBase, 1> BitfieldSmall; 
typedef Bitfield<BitfieldLargeBase, 2> BitfieldLarge; 
0

這個怎麼樣?

#include <limits.h> 

template <class T> 
union BitField 
{ 
    T bits; 
    unsigned all : sizeof(T) * CHAR_BIT; 
};