简体   繁体   中英

Casting struct to int in c++

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 uniont 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) ...

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.

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.

If the chosen implementation of this format in the OP isn't yet set-in-stone, perhaps this can help.

You could create a wrapper class that manages a word of the relevant size and returns/sets the relevant parts for a caller, as suggested by user3528438 .

Or you could do what I do (albeit not for CANs) and create multiple wrappers: For each 'subfield', make a struct holding a uint_or_whatever_t plus member functions/ operator s to extract the relevant bits. Then union multiple such struct s together.

Wait! Put away your type-punning pitchforks! The latter pattern is the exception, specifically allowed by the Standard for interleaved reads of members in the common initial sequence (look it up) of standard-layout struct s in a union - ensuring these are definitely accessing the same memory/not optimised away.

Having determined we're in Defined Behaviour Land, the real benefit is how this neatly avoids the implementation-defined layout of 'native' bitfields: you , not your compiler, define how relevant bits are get/set. Sure, you can manually do this in C, using inline bitmanip or helper functions - I used to - but by using a class, all the cruft is hidden in the class implementation, as it should be.

eg

class CanPriority {
    // the raw memory
    uint32_t m_raw;

public:
    // getter/setter methods or operators
    uint8_t get() const { /* extract, shift... */ }
    // etc.
};

class CanReserved {
    uint32_t m_raw;

public:
    uint8_t get() const { /* ... */ }
    // ...
};

union CanId {
    CanPriority priority;
    CanReserved reserved;
    // etc.
};

fwiw, I've done some pretty wild stuff with some very elaborate versions of this pattern for other cases - imagine what you can do when template s are added, for instance - and only ever had perfect results, even at -O3 -flto .

struct Id
{
    std::uint32_t bytes;

    // only considers least three significant bytes of x
    void priority(std::uint8_t x)
    { 
        bytes |= x & 0x03;
    }

    // only considers the least signifficant byte of x
    // sets byte into 4th bit of target value
    void reserved(std::uint8_t x)
    {
        bytes |= ((x & 0x01) << 4);
    }

    // ...
};

Client code:

Id id;
id.priority(0x1);
id.reserved(0x0); // reset the "reserved" bit to zero

In particular, it is dangerous to do this:

u8 priority         :3; 
u8 reserved         :1; 

The representation of these bits as you specify, is compiler-speciffic (and not part of the standard).

If it is only C++, then you can use reinterpret_cast :

struct canId {
    u8 priority         :3; 
    u8 reserved         :1; 
    u8 dataPage         :1;     
    u8 pduFormat        :8;     
    u8 pduSpecific      :8;     
    u8 sourceAddress    :8;     

    int &raw() {
        return *reinterpret_cast<int *>(this);
    }
} iD;

Then:

int data = iD.raw();

I guess that you wanted just to take this 29 bits from memory and pack it to int in one cast operation. I'm afraid you cannot be sure if your structure is packed in 29 bits, becouse reference says:

Multiple adjacent bit fields are usually packed together (although this behavior is implementation-defined)

http://en.cppreference.com/w/cpp/language/bit_field

So you need to use fritzone solution and write similar code for converting it back.

Another possible solution would be wrapping struct into integer initially and just wrap getters and setters to select right bits.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM