8

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.

Paindoo
  • 173
  • 3
  • 8
  • 2
    No, not at all, apart from everything, any padding will alone be enough to mess things up. – Sourav Ghosh Apr 22 '16 at 13:08
  • 2
    An union may be a good solution. – Boiethios Apr 22 '16 at 13:10
  • 5
    @Siguza It's bit-fields, so you have to look to the right of the colons. – unwind Apr 22 '16 at 13:11
  • 1
    I am pretty sure this isn't actually that easy. – Bartek Banachewicz Apr 22 '16 at 13:12
  • 3
    @Boiethios it's forbidden to read from a field of the union different that the one written to, so, no, it wouldn't be a good solution. – Bartek Banachewicz Apr 22 '16 at 13:13
  • If that is the format of your address, you have an unfriendly format. Even if you add whatever pragma is required to pack the struct, I don't know what your compiler will do with those 6 bytes at the start. In any case, I would be strongly tempted to unionise this with 32 bits. – Martin James Apr 22 '16 at 13:14
  • @Boiethios As Bartek said, union 'type punning' is not defined (maybe implementation-defined) in C++ - **unless** exception being you are interleaving operations on unioned `struct`s that share a _common initial sequence_; that is not the case here, but perhaps you could make it... Also, bitfield layout is implementation-defined making cross-platform assumptions difficult if you care about that. – underscore_d Apr 22 '16 at 13:14
  • 1
    Since this is a C++ question, why don't you make a wrapper class for it? – user3528438 Apr 22 '16 at 13:17
  • I've had no trouble using bitfieds to handle CAN addresses/messages, but I made sure that none of my fields spanned byte boundaries and that all structs were exact byte-mutiples. – Martin James Apr 22 '16 at 13:17
  • @user3528438 it's fixed by hardware. You cannot abstract it. – Martin James Apr 22 '16 at 13:18
  • @MartinJames But you could make a wrapper holding a `uint32_t` with functions/operators to extract the relevant bits, then `union` that with a bare `uint32_t`, thus satisfying the "common initial sequence" allowance without invoking implementation-defined layout of native bitfields. – underscore_d Apr 22 '16 at 13:18
  • 1
    If you don't care about portability and don't try to fiddle with the bits of the integer, a union will work. (Even a cast hack `*(int*)&Id`.) –  Apr 22 '16 at 13:19
  • The annoying this is that the CANID is probably coming out of a 32-bit register anyway:( – Martin James Apr 22 '16 at 13:23
  • 1
    I agree with @underscore_d, it's C++ so you don't have to use `id.priority=2;`, `id.setPriority(2);` is just as good, then you can make a wrapper class around `uint32_t` and implement those methods whatever way you want. And finally provide methods that convert from and to `uint32_t`. – user3528438 Apr 22 '16 at 13:23
  • @user3528438 Yup. I wrote an answer saying you could `union` this, but (A) there's no point as you could just provide public access to the raw basic type, and (B) I'm not sure basic types are technically included in the "common initial sequence" allowance for unions anyway. – underscore_d Apr 22 '16 at 13:25
  • 1
    You'll need to more thoroughly define _"copy this structure to an integer variable"_. – Lightness Races in Orbit Apr 22 '16 at 13:28
  • I would also like to ask why do you feel the need to use that structure. Can't you just store the thing as an `int` and extract data on demand? – Bartek Banachewicz Apr 22 '16 at 13:37
  • @BartekBanachewicz Hey, that almost sounds like user3528438's and my ideas about a single or multiple wrappers respectively ;-) – underscore_d Apr 22 '16 at 13:39

8 Answers8

4

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

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.

Bartek Banachewicz
  • 38,596
  • 7
  • 91
  • 135
2

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

Ferenc Deak
  • 34,348
  • 17
  • 99
  • 167
  • this is of course an option but it makes the use of bitfields struct useless in the first place. I edited the question. – Paindoo Apr 22 '16 at 13:20
  • @Paindoo: How does it make the use of bitfields "useless in the first place"? Pretty sure switching to using an `int` is what makes it useless, not this solution. – Lightness Races in Orbit Apr 22 '16 at 13:30
2

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.

Barry
  • 286,269
  • 29
  • 621
  • 977
  • 1
    I'm not convinced that's any better than a 32-bit union? – Martin James Apr 22 '16 at 13:20
  • 1
    @MartinJames You mean a `union` with `canId` and `uint32_t`? Well, that would be undefined behavior and this is not. – Barry Apr 22 '16 at 13:21
  • @Barry For a similar reason, I nixed my answer because I'm not sure a basic type is included in the "common initial sequence" allowance, which only explicitly mentions `structs`. – underscore_d Apr 22 '16 at 13:24
  • 3
    @Lightness Huh? `memcpy` is how you copy blocks of pod data. Would you prefer I used `std::copy` with `reinterpet_cast` to be more C++y? – Barry Apr 22 '16 at 13:47
  • @Barry: Yes. `memcpy` is how you propagate the ill-founded notion that writing `memcpy` in similar-but-not-quite-the-same situations is a good idea, and this elimination of type safety can be devastating to code maintenance. I found a `memcpy` once that had been blatting a `std::map` because somebody changed an object's type and didn't grep for all places where that object was being `memcpy` (because who would?). Best practice = AVOID! – Lightness Races in Orbit Apr 22 '16 at 13:54
1

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/operators to extract the relevant bits. Then union multiple such structs 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 structs 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.

e.g.

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 templates are added, for instance - and only ever had perfect results, even at -O3 -flto.

underscore_d
  • 6,309
  • 3
  • 38
  • 64
  • Can you actually write code? It sounds like you're describing a `union` holding two `uint32_t`s. – Barry Apr 22 '16 at 13:21
  • I doooon't think this helps at all. The `union` can't be used to convert the type, and that's it. – Bartek Banachewicz Apr 22 '16 at 13:22
  • @BartekBanachewicz It certainly can when accessing members that fall within the common initial sequence. – underscore_d Apr 22 '16 at 13:30
  • While this seems to be in line with the standard, I am not sure how does it actually help in the conversion from the original structure. – Bartek Banachewicz Apr 22 '16 at 13:33
  • @BartekBanachewicz Perhaps the structure in the OP is not set in stone, in which case, this might be a better way to do it, which the OP just hadn't thought of. It's usually good to explore alternatives. – underscore_d Apr 22 '16 at 13:35
  • It's a CAN identifier. I believe it's set in stone by an industry standard. – Rob K Apr 22 '16 at 13:51
  • @RobK Of course, I meant the way the OP has chosen to implement the format in C using na(t)ive bitfields. My point is that there are more deterministic ways to implement the same format, free of the burden of the implementation-defined order/packing/etc of native bitfields. – underscore_d Apr 22 '16 at 13:55
1
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).

utnapistim
  • 26,809
  • 3
  • 46
  • 82
1

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();
vlk
  • 2,581
  • 3
  • 31
  • 35
0

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.

Community
  • 1
  • 1
alxio
  • 128
  • 4