我有一些结构包含一个位域,其大小可能不同。例如:
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是不可能的。有没有办法用整数类型声明“all”字段,其大小与位域相同?或者另一种方式来访问整个位?
我当然可以放弃使用模板并明确声明每种类型的位域,但我想避免代码重复(有很多运算符和成员函数列表)。
答案 0 :(得分:5)
您也可以将积分类型设为模板参数。
template<typename T, typename U>
union Bitfield
{
T bits;
U all;
}
typedef Bitfield<BitfieldSmallBase, uint8_t> BitfieldSmall;
typedef Bitfield<BitfieldLargeBase, uint16_t> BitfieldLarge;
答案 1 :(得分:1)
我已经学到了很难的方法,虽然您正在使用的变量上的位宽是让编译器为您进行屏蔽和转换的便捷方式,但无法做出假设关于结构中成员的顺序和填充。它依赖于编译器,编译器确实改变了顺序,这取决于项目中的其他代码。
如果你想把一个字节视为离散字段,你真的必须这么做。
答案 2 :(得分:1)
您可以使用模板元编程来定义一个模板函数,该函数从BitfieldSmallBase,BitfieldLargeBase等映射到另一个类型 - 默认情况下为uint8_t,而对于BitfieldLargeBase则为uint16_t作为模板特化,然后使用如下:
union Bitfield
{
T bits;
typename F<T>::holder_type all;
};
答案 3 :(得分:1)
您可能需要考虑std::bitset或boost::dynamic_bitset而不是自己动手。无论如何,避开std::vector<bool>
!
答案 4 :(得分: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;
答案 5 :(得分:0)
这个怎么样?
#include <limits.h>
template <class T>
union BitField
{
T bits;
unsigned all : sizeof(T) * CHAR_BIT;
};