0

I wrote this code just for the fun of it:

union {
    struct {
        bool a : 1;
        bool b : 1;
        bool c : 1;
        bool d : 1;
        bool e : 1;
        bool f : 1;
        bool g : 1;
        bool h : 1;
    } a;
    uint8_t b;
} uni {0}; // sets .a bits to false

// union size  : 1 byte

I can toggle each bit individually:

uni.a.a = true;
uni.a.d = true;

cout << bitset<8>(uni.b) << endl; // Prints 000010001

And also use bit masks:

uni.b = 0b00001000;

if(uni.a.a) cout << "a";
if(uni.a.b) cout << "b";
if(uni.a.c) cout << "c";
if(uni.a.d) cout << "d";
if(uni.a.e) cout << "e";
if(uni.a.f) cout << "f";
if(uni.a.g) cout << "g";
if(uni.a.h) cout << "h";
// prints 'd'

I know there are other ways to do this (bitwise operations) but I like this design. My question is: is this a good use of bitfields and unions, in terms of reliability and performance?

Rm02
  • 11
  • 4
  • 2
    Both your example uses are invalid and leads to *undefined behavior*. C++ doesn't allow type-punning using unions. If you write to a member of a union, you can can read only from that member and no other. – Some programmer dude Mar 18 '18 at 17:18
  • 1
    It is unclear whether this is undefined behavior. https://stackoverflow.com/questions/11373203/accessing-inactive-union-member-and-undefined-behavior – Rm02 Mar 18 '18 at 17:28
  • 2
    That was a good find. However I would still argue that it's UB, but now from another angle. From the end of that answer: "... we can legitimately form an lvalue to a non-active union member ... it is considered to be uninitialized." Reading uninitialized data in C++ is also UB. – Some programmer dude Mar 18 '18 at 17:38
  • Just out of curiosity, is this trying to get a performance gain over `std::bitset` or what is the motivation? – super Mar 18 '18 at 17:40
  • 1
    Your link seems to say pretty clear that this is reading an object not within its lifetime and hence UB. – Passer By Mar 18 '18 at 17:42
  • No particular motivation whatsoever, I just want to know if this design is correct/useful. – Rm02 Mar 18 '18 at 17:43
  • A struct or union of bit fields is considered an integral data type, not Boolean. Initialize it with 0, not `false`. – Thomas Matthews Mar 18 '18 at 18:21
  • 1
    I recommend using *bit masking* or *bit testing* rather than using a union or bit fields. You don't know which bitfield is the MSB and which is the LSB. See the `&` operator. – Thomas Matthews Mar 18 '18 at 18:22
  • There is **no** good use for bit fields and (type punning via) unions. – n. m. could be an AI Mar 18 '18 at 19:13

1 Answers1

1

My question is: is this a good use of bitfields and unions, in terms of reliability and performance?

In terms of performance, what you are doing is probably as good as it is going to get, at least in terms of accessing memory at the bit level.

In terms of reliability, it is probably OK across most modern systems. At the risk of sounding like a language lawyer, here are some areas where it could have different results across systems:

  1. sizeof(bool) isn't guaranteed to be 1, that is bool can be stored in memory larger than one byte. I don't know of any modern system where bool isn't the size of a byte, but you could imagine an older or bad implementation that makes bool to be the same size as an int. This would inherently throw off your union.
  2. Accessing a different member variable of a union can caused undefined behavior
  3. The byte order across systems can be different (e.g., big or little endian), leading to differences in which member of the union would represent the MSB.
  4. Technically the implementation can lay out the individual bits of a bit field in any order within a byte. Every implementation I know would order them in the order you defined it in the struct, but that doesn't have to happen.
  5. The implementation can insert padding between individual members of the struct such that individual bits are in different bytes of memory. Again, all implementations that I know of won't do this but it can happen.

I don't think you would need to worry about the above. But, in terms of covering everything that could go wrong, I think the above list pretty well covers the really odd-ball scenarios that you could face if you really searched for an implementation that was a bit different.

Daniel
  • 1,291
  • 6
  • 15