1 module nxt.aggregate_layout;
2 
3 // version = show;
4 
5 @safe:
6 
7 void printLayout(T)()
8 if (is(T == struct) ||
9     is(T == class) ||
10     is(T == union))
11 {
12     import std.stdio;
13     import std.string;
14 
15     writefln("=== Memory layout of '%s'" ~
16              " (.sizeof: %s, .alignof: %s) ===",
17              T.stringof, T.sizeof, T.alignof);
18 
19     /* Prints a single line of layout information. */
20     void printLine(size_t offset, string info) {
21         writefln("%4s: %s", offset, info);
22     }
23 
24     /* Prints padding information if padding is actually
25      * observed. */
26     void maybePrintPaddingInfo(size_t expectedOffset,
27                                size_t actualOffset) {
28         if (expectedOffset < actualOffset) {
29             /* There is some padding because the actual offset
30              * is beyond the expected one. */
31 
32             const paddingSize = actualOffset - expectedOffset;
33 
34             printLine(expectedOffset,
35                       format("... %s-byte PADDING",
36                              paddingSize));
37         }
38     }
39 
40     /* This is the expected offset of the next member if there
41      * were no padding bytes before that member. */
42     size_t noPaddingOffset = 0;
43 
44     /* Note: __traits(allMembers) is a 'string' collection of
45      * names of the members of a type. */
46     foreach (memberName; __traits(allMembers, T)) {
47         mixin (format("alias member = %s.%s;",
48                       T.stringof, memberName));
49 
50         const offset = member.offsetof;
51         maybePrintPaddingInfo(noPaddingOffset, offset);
52 
53         const typeName = typeof(member).stringof;
54         printLine(offset,
55                   format("%s %s", typeName, memberName));
56 
57         noPaddingOffset = offset + member.sizeof;
58     }
59 
60     maybePrintPaddingInfo(noPaddingOffset, T.sizeof);
61 }
62 
63 version(show)
64 @safe unittest
65 {
66 	struct S
67     {
68         int i;                  // 4 bytes
69         short s;                // 2 byte
70         bool b;                 // 1 byte
71     }
72     static assert(S.sizeof == 8);
73     static assert(S.alignof == 4);
74     align(4) struct T
75     {
76         align(4) S s;
77         align(1) char c;
78     }
79     static assert(T.alignof == 4);
80     // TODO: static assert(T.sizeof == 8);
81     printLayout!(T)();
82 }
83 
84 // https://forum.dlang.org/post/jzrztbyzgxlkplslcoaj@forum.dlang.org
85 @safe pure unittest
86 {
87     struct S
88     {
89         int i;                  // 4 bytes
90         short s;                // 2 byte
91         bool b;                 // 1 byte
92     }
93     static assert(S.sizeof == 8);
94     static assert(S.alignof == 4);
95 
96     struct T
97     {
98         union
99         {
100             S s;
101             struct
102             {
103                 align(1):
104                 ubyte[7] _ignore_me;
105                 char c;
106             }
107         }
108     }
109 
110     static assert(T.alignof == 4);
111     static assert(T.sizeof == 8);
112     static assert(T.c.offsetof == 7);
113 }