Unions are problematic in that they will require explicit calls to union_reinterpret() whenever access moves between union members. Often though, unions are not used as disjoint unions but rather to achieve multiple views on the same data. For these cases, flattening of these unions helps to simplify the handling of these unions significantly.

Consider the following example, where a union is used to simplify the access to a bitfield structure:
typedef unsigned __int64 UINT64;

union U {
  struct {
    UINT64 a:16;
    UINT64 b:16;
    UINT64 c:16;
    UINT64 d:16;
  };

  UINT64 AsUINT64;
};


The intention of U is not to store qualitatively different values in both of its members. Instead, AsUINT64 is introduced as a union member that allows the assignment of values of type U in a single statement, e.g., as in u.AsUINT64 = 0;

In cases like this, the compiler can be told to flatten the union by designating a backing member using the backing_member attribute as follows:
typedef unsigned __int64 UINT64;

union U {
  struct {
    UINT64 a:16;
    UINT64 b:16;
    UINT64 c:16;
    UINT64 d:16;
  };

  backing_member UINT64 AsUINT64;
};


The backing member attribute will make the compiler remove other members from the union U, which thus turns into a union with only a single member, which, in turn, is treated as a struct. Accesses to the other fields (here: the field of the anonymous structure) are turned into bitfield accesses into AsUINT64.

For this transformation to work, it is required that the union type contains a field of integer type of a size that is sufficiently large to span all of the type. Also, other member of structured type may not have their own invariant (as these members are effectively inlined into the union, thus making it impossible to enforce their invariant).

It is, of course, still possible to express invariants on the level of U even when it involves fields are a removed in the flattening because those field expressions will be rewritten in terms of the surviving backing member.

Union members of structured type can also serve as a backing member, like in the following example:
typedef unsigned __int32 UINT32;
typedef unsigned __int64 UINT64;

union U {
  struct {
    UINT64 a:16;
    UINT64 b:16;
    UINT64 c:16;
    UINT64 d:16;
  };

  backing_member struct {
    UINT32 aa;
    UINT32 bb;
  };
};

Last edited Nov 13, 2009 at 12:03 PM by stobies, version 3

Comments

No comments yet.