Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Casting struct to int in c++

Tags:

c++

can-bus

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.

like image 971
Paindoo Avatar asked Apr 22 '16 13:04

Paindoo


4 Answers

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) ...
like image 149
Yves Daoust Avatar answered Oct 13 '22 00:10

Yves Daoust


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.

like image 37
Bartek Banachewicz Avatar answered Oct 12 '22 23:10

Bartek Banachewicz


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

like image 2
Ferenc Deak Avatar answered Oct 12 '22 23:10

Ferenc Deak


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.

like image 2
Barry Avatar answered Oct 12 '22 23:10

Barry