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