Reputation: 6032
I have a structure which looks like this:
#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
{}
};
These structs have an operator==
using memcmp
, so it's important that all bits are set to a known value in the constructor.
Recently, I realized that h_
and in_
are doing the same thing except in reverse, i.e. assert(h_ == !in_)
always holds true. I'm about to remove h_
and I'd like some compile-time check that would warn me that pad2_
needs to be extended from : 1
to : 2
.
Is this possible? I couldn't think of anything (sizeof(bits)
would be the same with or without the h_
field).
Upvotes: 0
Views: 563
Reputation: 9203
There is as such no way to query of the offsets of bitfield. So it would be difficult to create any constraints on the bitfields. Although the following pre processing trick can help you achieve what you need.
What you essentially need to do is create your struct fields using macros.
Your normal struct with 2 bit fields can be made as follows
#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
};
Everything fine till now. Now what we will do is create an auxiliary struxt which has parallel elements but each being n bytes as
#undef FIELD
#define FIELD(x,y) char x[y];
struct aux_##STRUCT_NAME {
FIELD_LIST
};
Cool. Now we have an advantage. We can take sizeof
of this aux struct and check if it is multiple of 8
static_assert(sizeof(struct aux_##STRUCT_NAME) % 8 == 0);
This now automatically checks if your bitfield struct has enough bit padding.
Finally to resuse this code you can add the actually generation code inside a .h file and in your main code, just redefine FIELD_LIST
and STRUCT_NAME
and include the header.
Upvotes: 1
Reputation: 30145
If you want to be sure of a particular size, etc. for efficnency, compatibility, etc., you could use static_assert
, e.g. static_assert(sizeof(bits) == 2, "Expected 16 bits")
and could potentially do likewise for each actual field.
More generally however for structs you will memcmp, etc. just make sure you use sizeof
everywhere with such functions. e.g. you could do.
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;
}
Upvotes: 0
Reputation: 67782
The only portable way to control exactly the layout and padding of your type, is to not use bitfields in the first place.
Eg, have some type wrapping an enum and std::bitset
instead. This means splitting your st_
member into three bits, or having a special accessor for that one field. Everything else is straightforward though, and the bit count is available to static_assert
at compile time.
Note that you still can't use memcmp
for this, but you get std::bitset::operator ==
for free.
Upvotes: 0
Reputation:
I think your best option is disregard padding, and bluntly set the entire memory layout to zero before assigning any values.
Something like this:
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;
...
}
Upvotes: 0