6

I have the following series of structs.

struct FooWord1
{
    unsigned int Fill      :  8;
    unsigned int someData1 : 18;
    unsigned int someData2 : 6;
};

struct FooWord2
{
    unsigned int Fill      :  8;
    union
    {
        unsigned int A_Bit : 1;
        unsigned int B_Bit : 1;
    };
    unsigned int someData3 : 23;
};

struct Foo_Data
{
    FooWord1 fooWord1;
    FooWord2 fooWord2;
    FooWord3 fooWord3;  // similar to FooWord1
    FooWord4 fooWord4;  // similar to FooWord1
    FooWord5 fooWord5;  // similar to FooWord1
};

Foo_Data fooArray[SIZE];

Data gets copied into fooArray byte-by-byte from a network message. We get the data we expect in someData3 if we don't use a union with the 1-bit field (A_bit and B_bit), but as soon as we put in the union, the words get "off" by 2 words.

We want to use a union there because these structures are used for different types of messages, but the meaning of A_Bit and B_Bit is different for different messages. We could just use a comment, but it would be nice to do it in code.

What am I doing wrong?

timrau
  • 22,578
  • 4
  • 51
  • 64
livefree75
  • 720
  • 8
  • 18
  • 1
    You seem to expect the union to occupy just one bit. That's not going to work. `FooWord2` has three members, one of which is an (anonymous) union, not a bit field, so those three members won't get packed together. The fact that the union in turn contains bit fields is irrelevant. – Igor Tandetnik Sep 01 '16 at 16:22
  • if you want to be able to use different names to refer to that bit, use macros. – Barmar Sep 01 '16 at 16:26
  • Side note: with your current code, there doesn't seem to be any need for a `union`, because all the fields in it are identical. – barak manos Sep 01 '16 at 16:38
  • 2
    I would avoid to use bit-fields to define messages of any communication protocol. The packing order of bit-fields into bytes/words is not mandated by C++ standard. Thus the first bit field in order of declaration could be put into the most significant bits of an int. – Serge Sep 01 '16 at 16:46
  • Addition to @Serge 's comment: ... and the other machine then might try to read the same bit value from the least significant bit in the message. – Aconcagua Sep 01 '16 at 16:59
  • Thanks for the comments. To @Serge, yes that is true. And we do have another facility to put things in the right place with respect to endianness. However, this is the way it was coded long ago, before we had people that understood this, and it works for us for now. Obviously, if we ever change platforms, we'll have to spend the $$$ to fix it. Right now we're limited by money and politics. :-/ – livefree75 Sep 01 '16 at 18:11
  • @IgorTandetnik Yes, I guess I was under the impression taht union {} puts multiple variables in the same place in memory. We do other things like struct { FooWord1 fooWord1; union { FooWord2a fooWord2a; FooWord2b fooWord2b; }; FooWord3 fooWord3l } And that seems to work "as expected". – livefree75 Sep 01 '16 at 18:13
  • @livefree75 it is not in regard of architecture endianness. The C++ standard says it is implementation defined, so you may have different results for the same arch with different compilers. – Serge Sep 01 '16 at 18:16
  • 1
    The union does put `A_Bit` and `B_Bit` into the same place in memory - it's just that it's a different place in memory than where you wanted them to be. – Igor Tandetnik Sep 01 '16 at 18:21
  • @IgorTandetnik nevertheless it's undefined behavior in 'C++' – Serge Sep 01 '16 at 18:32
  • @Serge thanks for the clarification. So, right now we're only using one compiler. Again, it's a money / politics issue. There are those in our organization who would love to convert everything over to the "correct" way, but it's not a priority compared to new functionality and defect resolution. – livefree75 Sep 01 '16 at 19:01
  • @Serge What's undefined behavior in C++? – Igor Tandetnik Sep 01 '16 at 19:22
  • @IgorTandetnik Expecting the B_Bit has the same value as A_Bit after A_Bit change. – Serge Sep 01 '16 at 19:37
  • `Foo_Data[SIZE] fooArray;` is a syntax error. Also , based on your description, a better solution would be to have a union of two different structs for the two different messages – M.M Sep 01 '16 at 20:50
  • @Serge I don't believe the OP ever said that he or she wants to rely on that. They said they wanted `A_Bit` and `B_Bit` to occupy the same storage, but I don't see any indication that they plan to write one and then read the other. – Igor Tandetnik Sep 02 '16 at 05:04
  • @IgorTandetnik I just mentioned possible caveats – Serge Sep 02 '16 at 11:38
  • So, just out of curiosity, what couldn't I set one member of the union and expect the other member to return the same value, if they're occupying the same space in memory? – livefree75 Sep 02 '16 at 12:36
  • @M.M, yes you're right. i'll edit. – livefree75 Sep 02 '16 at 12:37
  • You could expect it, but the program is not guaranteed to provide it. In fact, once you do that, there are no guarantees anymore: reading an inactive member of the union exhibits undefined behavior in C++. See also: https://en.wikipedia.org/wiki/Type_punning – Igor Tandetnik Sep 02 '16 at 12:55

2 Answers2

0

You might try this:

struct FooWord2
{
    union
    {
        struct
        {
            unsigned int Fill      : 8;
            unsigned int A_Bit     : 1;
            unsigned int someData3 : 23;
        };
        struct
        {
            unsigned int       : 8;
            unsigned int B_Bit : 1;
        };
    };
};

Need to mention: according to this answer, anonymous structs are not C++ standard, but extension to. GCC permits, MSVC – as far as I remember – too. LLVM? Not sure, but as often close to GCC, would assume so, too.

In standard conform libraries, they ofen use macros to get the right effect, like this:

struct FooWord2
{
    unsigned int Fill      :  8;
    unsigned int A_Bit     :  1;
    unsigned int someData3 : 23;
};
#define B_Bit A_Bit

Just explanatory: With your original code, you got this effect:

  • Fill starts a new bit field
  • union is another data type, so previous bit field is finished
  • Inside union, a new bit field is started here I'm not sure if this creates one single bit or possibly a bit field with two entries. Perhaps anyone can shed some light on what the standard states to...
  • Closing the union, the bit field is finished, too.
  • someData3 then starts a new bit field

Thus the offsets you want to avoid.

Community
  • 1
  • 1
Aconcagua
  • 24,880
  • 4
  • 34
  • 59
0

The answer lies in the comments to the original question. Fill, the union, and someData3 will all wind up in separate words, because the union starts a new word in the struct.

livefree75
  • 720
  • 8
  • 18