aka.nice
aka.nice

Reputation: 9372

Is this a proper usage of union

I want to have named fields rather than indexed fields, but for some usage I have to iterate on the fields. Dumb simplified example:

struct named_states {float speed; float position;};

#define NSTATES (sizeof(struct named_states)/sizeof(float))
union named_or_indexed_states {
   struct named_states named;
   float indexed[NSTATES];
}
...
union named_or_indexed_states states,derivatives;
states.named.speed = 0;
states.named.position = 0;
...
derivatives.named.speed = acceleration;
derivatives.named.position= states.named.speed;
...
/* This code is in a generic library (consider nstates=NSTATES) */
for(i=0;i<nstates;i++)
    states.indexed[i] += time_step*derivatives.indexed[i];

This avoid a copy from named struct to indexed array and vice-versa, and replace it with a generic solution and is thus easier to maintain (I have very few places to change when I augment the state vector).It also work well with various compiler I tested (several versions of gcc/g++ and MSVC).

But theorically, as I understand it, it does not strictly adhere to proper union usage since I wrote named field then read indexed field, and I'm not sure at all we can say that they share same struct fields...

Can you confirm that's it's theorically bad (non portable)?

Should I better use a cast, a memcpy() or something else?

Apart theory, from pragmatic POV is there any REAL portability issue (some incompatible compiler, exotic struct alignment, planned evolutions...)?

EDIT: your answers deserve a bit more clarification about my intentions that were:

I need to know

Upvotes: 4

Views: 327

Answers (3)

Griwes
Griwes

Reputation: 9031

Only accessing last written member of union is well-defined; the code you presented uses, as far as only standard C (or C++) is concerned, undefined behavior - it may work, but it's wrong way to do it. It doesn't really matter that struct uses the same type as the type of array - there may be padding involved, as well as other invisible tricks used by compiler.

Some compilers, like GCC, do define it as allowed way to achieve type-punning. Now the question arises - are we talking about standard C (or C++), or GNU or any other extensions?

As for what you should use - proper conversion operators and/or constructors.

Upvotes: 2

Coder_Dan
Coder_Dan

Reputation: 1872

This may be a little old-fashioned, but what I would do in this situation is:

enum {

F_POSITION,

F_SPEED,

F_COUNT };

float states[F_COUNT];

Then you can reference them as: states[F_POSITION] and states[F_SPEED].

That's one way that I might write this. I'm sure that there are many other possibilities.

Upvotes: 1

Pete Becker
Pete Becker

Reputation: 76245

There's no requirement that the two fields in named_states line up the same way as the array elements. There's a good chance that they do, but you've got a compiler dependency there.

Here's a simple implementation in C++ of what you're trying to do:

struct named_or_indexed_states {
    named_or_indexed_states() : speed(indexed[0], position(indexed[1]) { }
    float &speed;
    float &position;
    float indexed[2];
};

If the size increase because of the reference elements is too much, use accessors:

struct named_or_indexed_states {
    float indexed[2];
    float& speed() { return indexed[0]; }
    float& position() { return indexed[1]; }
};

The compiler will have no problem inlining the accessors, so reading or writing speed() and position() will be just as fast as if they were member data. You still have to write those annoying parentheses, though.

Upvotes: 3

Related Questions