我有一个看起来像这样的结构:
#include <stdint.h>
struct bits {
uint8_t u_ : 1;
uint8_t k_ : 1;
uint8_t c_ : 1;
uint8_t x_ : 1;
uint8_t b_ : 1;
uint8_t ns_ : 1;
uint8_t ms_ : 1;
uint8_t as_ : 1;
uint8_t /*padding1*/ : 0;
uint8_t st_ : 3;
uint8_t su_ : 1;
uint8_t ig_ : 1;
uint8_t h_ : 1;
uint8_t in_ : 1;
uint8_t pad2_ : 1;
bits_t(uint8_t u, uint8_t k, uint8_t c, uint8_t x, uint8_t b,
uint8_t n, uint8_t m, uint8_t a, uint8_t s, uint8_t i,
bool h, bool in)
: u_ { u }
, k_ { k }
, c_ { c }
, x_ { x }
, b_ { b }
, ns_ { n }
, ms_ { m }
, as_ { a }
, st_ { 0 }
, su_ { s }
, ig_ { i }
, h_ { h }
, in_ { in }
, pad2_ { 0 } // carefully initialize all bits
{}
};
这些结构使用operator==
memcmp
,因此在构造函数中将所有位设置为已知值非常重要。
最近,我意识到h_
和in_
正在做同样的事情,除了相反,即assert(h_ == !in_)
总是成立。我即将删除h_
,我想要一些编译时检查,警告我pad2_
需要从: 1
扩展到: 2
。
这可能吗?我无法想到任何事情(sizeof(bits)
无论是否有h_
字段都是相同的。
答案 0 :(得分:1)
因此无法查询位域的偏移量。因此,很难在位域上创建任何约束。虽然以下预处理技巧可以帮助您实现所需。
您基本上需要做的是使用宏创建结构字段。
具有2位字段的普通结构可以按如下方式进行
#define STRUCT_NAME my_bitfield
#define FIELD_LIST \
FIELD(a,3) \
FIELD(b,5)
#define FIELD(x,y) int x:y;
struct STRUCT_NAME {
FIELD_LIST
};
到目前为止一切都很好。现在我们要做的是创建一个辅助的struxt,它有并行元素但每个都是n个字节
#undef FIELD
#define FIELD(x,y) char x[y];
struct aux_##STRUCT_NAME {
FIELD_LIST
};
冷却。现在我们有一个优势。我们可以取这个aux结构的sizeof
并检查它是否是8的倍数
static_assert(sizeof(struct aux_##STRUCT_NAME) % 8 == 0);
现在会自动检查你的位域结构是否有足够的位填充。
最后要重新使用此代码,您可以在.h文件和主代码中添加实际生成代码,只需重新定义FIELD_LIST
和STRUCT_NAME
并包含标题。
答案 1 :(得分:0)
我认为你最好的选择是忽略填充,并在分配任何值之前直接将整个内存布局设置为零。
这样的事情:
bits_t::bits_t(uint8_t u, uint8_t k, uint8_t c, uint8_t x, uint8_t b,
uint8_t n, uint8_t m, uint8_t a, uint8_t s, uint8_t i, bool h, bool in) {
//Sanity check that memset(this) is valid
static_assert(std::is_pod<bits_t>::value, "");
memset(this, 0, sizeof(*this));
u_ = u;
...
}
答案 2 :(得分:0)
如果您想确定特定尺寸等的效率,兼容性等,可以使用static_assert
,例如static_assert(sizeof(bits) == 2, "Expected 16 bits")
并且可能对每个实际领域都这样做。
更一般地说,对于结构,你将memcmp等,只是确保你使用sizeof
到处都有这样的功能。例如你能做到。
struct bits
{
... fields ....
bits()
{
memset(this, 0, sizeof(*this));
}
bits(bool flag_a)
: bits()
{
// doesnt matter if some were missed, other constructor called memset
_flag_a = flag_a;
}
bits(const bits &cp)
{
// important, default copy constructor, operator, etc. may leave padding as anything!
memcpy(this, &cp, sizeof(*this));
}
};
bool operator == (bits &lhs, bits &rhs)
{
return memcmp(&lhs, &rhs, sizeof(bits)) == 0;
}
bool operator != (bits &lhs, bits &rhs)
{
return memcmp(&lhs, &rhs, sizeof(bits)) != 0;
}
答案 3 :(得分:0)
唯一能够精确控制类型布局和填充的便携式方法是首先不使用位域。
例如,有一些类型包装枚举和std::bitset
。这意味着将st_
成员分成三个位,或者为该一个字段设置一个特殊的访问者。其他一切都很简单,编译时static_assert
可以使用位数。
请注意,您仍然无法使用memcmp
,但您可以免费获得std::bitset::operator ==
。