2

I have a union containing a uint16 and a struct like so:

union pData {
    uint16_t w1;
    struct {
        uint8_t d1 : 8;
        uint8_t d2 : 4;
        bool b1 : 1;
        bool b2 : 1;
        bool b3 : 1;
        bool b4 : 1;
    } bits;
};

My colleague says there is an issue with this being portable, but I am not sure I buy this. Could some please explain (simple as possible) what is "wrong" here?

xBACP
  • 531
  • 1
  • 3
  • 17
  • Some reading material about that: https://stackoverflow.com/questions/1044654/bitfield-manipulation-in-c As long as all of your platforms are same endian and you're serailizing them through the `w1` variable in your example you're probably okay, but it's often a pain to figure out when it doesn't work right. Probably not as much an issue now but older console compilers had issues with them so we stopped using them. – Retired Ninja Oct 31 '18 at 00:50
  • Why do you use an old C++ standard? – curiousguy Oct 31 '18 at 00:52
  • @curiousguy, not everyone lives at the bleeding edge :-) We have systems that are still built in C++11 mode, though we're *gradually* migrating to more advanced stuff. – paxdiablo Oct 31 '18 at 00:55
  • We have some that are sorta c++11ish as long as you don't get too fancy. People laughed when I said we'd finally get to use c++11 around 2020. – Retired Ninja Oct 31 '18 at 01:07

2 Answers2

5

From C++17 12.2.4 Bit-fields /1 (and C++11 9.6 Bit-fields /1 for that matter, if you want the answer specific to your chosen tags):

Allocation of bit-fields within a class object is implementation-defined. Alignment of bit-fields is implementation-defined. Bit-fields are packed into some addressable allocation unit. [Note: Bit-fields straddle allocation units on some machines and not on others. Bit-fields are assigned right-to-left on some machines, left-to-right on others. - end note]

Reliance on implementation defined behaviour means, by its very nature, non-portable code.

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
  • Implementation specified doesn't mean non-portable *unless you rely on yhe imolementation defined stuff*. In this case though, looks like this relies on it, so just nitpicking since you said "by definition non-portable". Also static asserts csn help make the code semi-portable (eiher it will work or it will not compile). – hyde Oct 31 '18 at 05:32
  • 1
    @hyde, you seem to be stating that it's portable as long as you don't rely on the implementation-defined behaviour. That seems to me to be a tautology, or redundant, or whatever the technical term is :-) As you state, the OP *is* using implementation-defined stuff so it *is* inherently non-portable. I'll update the answer to make it clearer that *use* of IDB makes it so. – paxdiablo Oct 31 '18 at 07:09
  • Yeah, I guess the point I am trying to make is, as a different example: writing a POD struct to disk or socket and reading it back is just fine even if struct layout is implementation defined, as long as reading and writing is done by compatible implementations. In this example case, the *code* is portable, even if it relies on certain implementation having written the data. Doesn't apply to OPs case though. – hyde Oct 31 '18 at 07:14
1

Maybe your colleague guesses that you intend to write to w1 and read from bits or vice versa.

That would be undefined behaviour. In C++ only one member of a union can be active at any one time; writing a member makes it active, and the behaviour of reading an inactive member is undefined.

M.M
  • 138,810
  • 21
  • 208
  • 365