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.
To create a structure with two views into the same data, we need to execute the following steps:
Colorstruct
, we'll want it to be represented by an array of ubytes
with each byte also having a name.struct
with each named member.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
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.
3.145.71.115