0

I got a bit field with a bunch of flags, and I need a quick and dirty way to set everything to zero, so instead of blindly casting the struct to an integer, I decided it would be "better" to put the bit fields in a union with an actual integer.

union Flags {
    uint _all;
    struct {
        uint status : 2;
        uint expanded : 1;
        uint draw : 1;
        uint drawChildren : 1;
        uint hidden : 1;
        uint disabled : 1;
        uint used : 1;
        uint deletable : 1;
        uint incomplete : 1;
        uint isStatic : 1;
        uint isConst : 1;
        uint isVolatile : 1;
        uint isInline : 1;
        uint isMutable : 1;
        uint isExtern : 1;
        uint isRegister : 1;
        uint threadLocal : 1;
        uint packed : 1;
        uint dirty : 1;
        uint native : 1;
        uint dynamic : 1; // 22
        uint _padding : 10;
    } flags;
};

My question is how portable is this? Can I expect it to be portable across different platforms (mostly interested in windows, linux, macos, android, ios) using GCC as a compiler?

Or maybe casting to an integer and setting it this way and getting rid of the union is the way to go? I keep reading that bit fields are not portable, and yet for example Qt seems to use them quite a lot and it does seem to work uniformly across the platforms I listed.

Last but not least, even without the union, can I expect the bit fields to be portable?

EDIT: Could not add C as a tag, but I need this to work in C as well, so I can't use std::bitset, also I have a member that takes more than one bit.

EDIT 2: Also note that none of the members crosses its alignment boundary, which I assume should cause the compiler to not add extra padding.

EDIT 3: Maybe I can use GCC's __attribute__(packed) to prevent the compiler from mangling with the structure?

timrau
  • 22,578
  • 4
  • 51
  • 64
  • You should use `uint32_t` instead of `uint` to make sure that you're really using a 32bit unsigned integer. Other than that, bit fields are usually a hazzle. That being said, since you already use C++, why don't you use `std::bitset` as storage? – Zeta Jan 25 '14 at 13:02
  • @Zeta - I would have added C as a tag too if more than 5 tags are allowed, since I need it to also work in C, making `bitset` a no-go. –  Jan 25 '14 at 13:06
  • I would recommend wrapping this in a class. Use a private `std::bitset` for storage, and accessor methods for your symbolic names. – tenfour Jan 25 '14 at 13:19
  • @tenfour - can't use that, it seems I will have to go the long way and use shifting and masking... –  Jan 25 '14 at 13:20

2 Answers2

2

Bit fields aren't portable at all. The padding in between them depends on implementation. Maybe you want a std::bitset

Paul Evans
  • 27,315
  • 3
  • 37
  • 54
  • I've padded it explicitly to prevent the compiler from doing so. And since I use a single compiler, I expect the implementation to be uniform, regardless of endianess. –  Jan 25 '14 at 13:03
  • Also, I need that to work in C as well, so I can't use `std::bitset` –  Jan 25 '14 at 13:07
  • 1
    @user2341104 When you say "the compiler" you mean just that - your specific compiler, for your specific platform. Another compiler is free to do a different thing with your bit fields - [for example, it can legally allocate them in the opposite order to what your current compiler does](http://stackoverflow.com/q/6043483/335858). – Sergey Kalinichenko Jan 25 '14 at 13:07
  • So, what you are telling me is that a compiler 26 years in development cannot consider endianess and generate portable code for bitfields? That sounds terrible... –  Jan 25 '14 at 13:11
  • 1
    @user2341104 Arr... there be many more monsters and dragons when you dare sail into the deep dark waters of C/C++ ;) – Paul Evans Jan 25 '14 at 13:41
  • IDK, maybe it is just me, but what the H is the purpose of a feature that is not guaranteed to work? Considering that bit fields indeed CAN be implemented in a uniform and portable way, I cannot help but wonder WHY IT WAS NOT. Because people need to shoot their feet off more frequently? –  Jan 25 '14 at 13:46
  • @user2341104 it is you, but many people have got into trouble believing bit fields behave portably. [Alas it's not their purpose in the slightest](http://blog.aaronballman.com/2011/08/the-joys-of-bit-fields/)! – Paul Evans Jan 25 '14 at 13:54
  • Bitfields were meant for use by systems-level code, on a particular platform/ABI. That's why so much about them is implementation-defined (rather than either standard-defined or undefined). – Sneftel Jan 25 '14 at 13:59
  • @Sneftel - they should not be part of the "public" API then, nor part of the standard, since they appear to be about as far from "standard" as it gets. Because that is what standard means, a STANDARD! –  Jan 25 '14 at 14:01
  • @user2341104 bit fields have been very carefully standardised, you are just wanting them to be something totally different because that would suit your purpose. – Paul Evans Jan 25 '14 at 14:03
  • This is a logical paradox then, compliance to a standard implies uniformity, and with bit fields it doesn't seem like there is any uniformity in their behavior. So bit fields are not intended to store data on the low level when you need less than a byte? What are they intended for? Writing non-portable code? More important, what is the reason they decided it is better for bit fields to be that unreliable? I mean after all, it is just shifting and masking on registers, which, correct me if I am wrong, does work everywhere in the same manner. –  Jan 25 '14 at 14:11
  • @user2341104 bit fields are pretty-much entirely implementation dependant, just about every aspect is left up to the compiler writer precisely so they have the freedom not to march to your (or anybody else's) drum. Basically, bit fields will (probably) make a more compact memory footprint than the same `struct` without them. That's all you get. – Paul Evans Jan 25 '14 at 14:23
  • Freedom not to match simple logic and common sense apparently, it is not about my or anyone else's drum, it is about what makes sense. Or maybe it is a good thing if the each compiled does something different when you tell it to do the same thing? Like a shop keeper you tell "give be a six pack of beer" and he gets you something entirely different? –  Jan 25 '14 at 14:37
  • @user2341104 can't wait to see the melodrama when you discover the C/C++ compiler's free to rearrange and/or delete all your statements as it sees fit :) – Paul Evans Jan 25 '14 at 14:43
  • @PaulEvans - sorry do disappoint you, but no compiler is not allowed to do any of those changes in a way that breaks the user intent (unless it is a compiler BUG). Which bit fields apparently do. Nor is any compiler allowed to mangle with the binary representation of types with explicit width/size, which is something you define with bit fields. –  Jan 25 '14 at 14:45
  • 1
    Of course compilers are allowed to do that. Signed integers can be represented in any of three different systems; it's implementation-defined which one is used. C was not conceived as a language for cross-platform data transfer, and you're going to run into (not insurmountable) difficulties when you try to use it as one. – Sneftel Jan 25 '14 at 17:55
  • @PaulEvans: I'm not sure about the "carefully" part of your statement. Anything that can be done with bitfields could be done by a syntax which a struct to associate an identifier with part of a member (e.g. `struct fp_number { uint32_t first_word, second_word; unsigned mode = first_word:0.5, param = first_word.5:27; dest = second_word.0:11; src = second_word.11:21;};` As it is, bit-fields are too rigidly specified to allow an implementation to e.g. let someone store four six-bit values in three bytes, but too loosely specified to allow much benefit compared with a totally-opaque type. – supercat Sep 15 '15 at 20:11
2

Once you set one member of a union, the values of all other members are undefined, so this is technically not standards-compliant. If you want to set everything to zero, just use memset, which is entirely standards-compliant and furthermore will generally be interpreted as an intrinsic and give you the maximum possible performance. (And it's simpler.)

Sneftel
  • 40,271
  • 12
  • 71
  • 104