I have a structure to represent the 29 bit CAN identifier with bit fields as following.
struct canId
{
u8 priority :3;
u8 reserved :1;
u8 dataPage :1;
u8 pduFormat :8;
u8 pduSpecific :8;
u8 sourceAddress :8;
} iD;
in my code I want to copy this structure to an integer variable. Something like:
int newId = iD;
However I am not sure if this is correct. Can somebody comment on it?
Edit: I can do this using shift operator on each field and then with bitwise OR to have them in the right place. But that makes the use of structure with bit fields useless in the first place.
For a truly portable solution, you shouldn't be using a struct of bitfields at all, as the field layout is undefined. Instead, use an integer and explicit bitwise arithmetic.
But for a more relaxed solution, a union of an int with the bitfield struct is good enough.
To combine safety and convenience, you can also consider integrating a test function that toggles the fields one at a time and checks the match with the desired integers.
union
{
uint32_t packed;
struct canId
{
u8 priority :3;
u8 reserved :1;
u8 dataPage :1;
u8 pduFormat :8;
u8 pduSpecific :8;
u8 sourceAddress :8;
} split;
} iD;
iD.packed= 0; iD.split.priority= 7; if (iD.packed != 0x7) ...
The safest way I can imagine is doing it manually:
int canIdToInt(canId id) {
int temp = 0;
int offset = 0;
temp |= id.sourceAddress << offset; offset += 8;
temp |= id.pduSpecific << offset; offset += 8;
temp |= id.pduFormat << offset; offset += 8;
temp |= id.dataPage << offset; offset += 1;
temp |= id.reserved << offset; offset += 1;
temp |= id.priority << offset; // offset += 3; redundant
return temp;
}
Of course you could hide the whole offset thing behind a macro to make it a bit cleaner.
#define START_WRITE int temp=0,offset=0
#define RESULT temp
#define ADD_VALUE(X) temp |= X << offset; offset += sizeof(X)
Actually, sizeof
won't behave as expected here, but there's another macro that will.
I hope I did the bit magic correctly ... You might do something like:
newID = (int)iD.priority
| (int)(iD.reserved) << 3
| (int)(iD.dataPage) << (3 + 1)
| (int)(iD.pduFormat) << (3 + 1 + 1)
| (int)(iD.pduSpecific) << (3 + 1 + 1 + 8)
| (int)(iD.sourceAddress) << (3 + 1 + 1 + 8 + 8)
But for this you will need a system where int
has at least 32 bits
Setting aside the issue that ordering of bitfields is implementation defined, you could always pad out the bitfield to make it 32 bits and then memcpy
it into your int
:
struct canId
{
u8 priority :3;
u8 reserved :1;
u8 dataPage :1;
u8 pduFormat :8;
u8 pduSpecific :8;
u8 sourceAddress :8;
u8 _padding :3;
} iD;
int newId = 0;
static_assert(sizeof(iD) <= sizeof(newId), "!");
memcpy(&newId, &iD, sizeof(iD));
This is perfectly well-defined behavior. Type-punning through a union
is not.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With