如何检查位域是否跨越所有位

时间:2017-08-22 14:53:42

标签: c++

我有一个看起来像这样的结构:

#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_字段都是相同的。

4 个答案:

答案 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_LISTSTRUCT_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 ==