Creating a structure with two views into the same data

Sometimes, it is useful to have a structure with multiple representations. This may be achieved with properties, but there is also a more direct way of doing it: using unions and anonymous structs. We'll create a Color structure with both RGBA and array representations.

How to do it…

To create a structure with two views into the same data, we need to execute the following steps:

  1. Write a struct to contain your type.
  2. Add a union without a name to hold the alternative representations.
  3. In the union, add the views. Be sure they match up in size and binary layout. For our Colorstruct, we'll want it to be represented by an array of ubytes with each byte also having a name.
  4. For the names, write a nameless struct with each named member.
  5. When using the structure, use the names directly.

To implement the preceding steps, we will have to execute the following code:

struct Color {
    union {
        ubyte[4] rgba;
        struct {
            ubyte r;
            ubyte g;
            ubyte b;
            ubyte a;
         }
    }
}
Color c;
c.r = 100; // accessing the inner name is easy
assert(c.rgba[0] == 100); // alternative view into same data

How it works…

D will permit you to group members inside an aggregate by wrapping them in other anonymous aggregates. Here, we wanted a struct that exposed two representations of the same data. A union is an aggregate type where all the members overlap in memory. This may be used to share memory between types where only one is valid at any time, as we did in the dynamic type, or it can be used to provide multiple equally legitimate views into the same data, which is what we're doing here.

The first view is an array of 4 bytes. The second view gives individual names to each byte. For that, we used a struct with four individual ubyte members. Thus, we have different views into the same memory.

Since we didn't give the inner union nor struct a name (this is only permitted inside another struct, union, or class), it serves to group the members in the implementation, but is invisible to the user. We do not need to use an additional dot member nor declare a separate variable of the anonymous type.

This technique would also be useful to access hardware-mapped memory or a task such as writing an x86 emulator, where the registers may be represented with several layers of nested anonymous structs and unions to provide access to each individual byte as well as the 16, 32, and 64 bit views (AX, EAX, and RAX, for example).

Let's execute the following code:

struct x86Accumulator {
  union {
    long RAX;
    // lowest items first because x86 is little endian
    struct {
      union {
        struct {
          union {
            struct {
              byte AL;
              byte AH;
            }
            short AX;
          }
          short upperWord;
        }
        int EAX;
      }
     int upperDWord;
    }
  }
}

void main() {
    x86Accumulator a;
    import std.stdio;
    a.EAX = 50;
    // set the high byte of the low word independently
    // equivalent to adding 256...
    a.AH = 1;
    writeln(a.RAX); // should be 306
}

We can update or view individual parts, which simultaneously affects the whole object.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.71.115