1 /** Trait for getting maximum size of types `T`.
2  *
3  * Implementation in `std.variant` uses recursion.
4  *
5  * See_Also: https://forum.dlang.org/post/hzpuiyxrrfasfuktpgqn@forum.dlang.org
6  */
7 module nxt.maxsize_trait;
8 
9 // version = nxt_benchmark;
10 
11 /** Get maximum size of types `Ts`.
12  *
13  * Limitation compared to `std.variant.maxSize`: `Ts` cannot contain `void`.
14  */
15 static template maxSizeOf(Ts...)
16 {
17 	align(1) union Impl { Ts t; }
18 	enum maxSizeOf = Impl.sizeof;
19 }
20 
21 ///
22 pure @safe unittest {
23 	static assert(maxSizeOf!(char) == 1);
24 	static assert(maxSizeOf!(byte) == 1);
25 	static assert(maxSizeOf!(byte, short) == 2);
26 	static assert(maxSizeOf!(short, byte) == 2);
27 	static assert(maxSizeOf!(byte, short, int) == 4);
28 	static assert(maxSizeOf!(byte, short, int, long) == 8);
29 	static assert(maxSizeOf!(byte, short, int, string) == 16);
30 	static assert(!__traits(compiles, { enum _ = maxSizeOf!(byte, void); }));
31 }
32 
33 // alternative implementation that supports `void`
34 static template maxSizeOf_1(Ts...)
35 {
36 	align(1) union Impl {
37 		static foreach (i, T; Ts) {
38 			static if (!is(T == void))
39 				mixin("T _field_" ~ i.stringof ~ ";");
40 		}
41 	}
42 	enum maxSizeOf_1 = Impl.sizeof;
43 }
44 
45 ///
46 pure @safe unittest {
47 	static assert(maxSizeOf_1!(char) == 1);
48 	static assert(maxSizeOf_1!(byte) == 1);
49 	static assert(maxSizeOf_1!(byte, short) == 2);
50 	static assert(maxSizeOf_1!(short, byte) == 2);
51 	static assert(maxSizeOf_1!(byte, short, int) == 4);
52 	static assert(maxSizeOf_1!(byte, short, int, long) == 8);
53 	static assert(maxSizeOf_1!(byte, short, int, string) == 16);
54 	static assert(maxSizeOf_1!(byte, void) == 1);
55 	static assert(maxSizeOf_1!(byte, short, void) == 2);
56 }
57 
58 template maxSizeOf_2(Ts...)
59 {
60 	enum maxSizeOf_2 = compute();
61 	auto compute()
62 	{
63 		size_t result;
64 		static foreach (T; Ts)
65 			if (T.sizeof > result)
66 				result = T.sizeof;
67 		return result;
68 	}
69 }
70 
71 ///
72 pure @safe unittest {
73 	static assert(maxSizeOf_2!(char) == 1);
74 	static assert(maxSizeOf_2!(byte) == 1);
75 	static assert(maxSizeOf_2!(byte, short) == 2);
76 	static assert(maxSizeOf_2!(short, byte) == 2);
77 	static assert(maxSizeOf_2!(byte, short, int) == 4);
78 	static assert(maxSizeOf_2!(byte, short, int, long) == 8);
79 	static assert(maxSizeOf_2!(byte, short, int, string) == 16);
80 	static assert(maxSizeOf_2!(byte, void) == 1);
81 	static assert(maxSizeOf_2!(byte, short, void) == 2);
82 }
83 
84 struct W(T, size_t n)
85 {
86 	T value;
87 }
88 
89 version (nxt_benchmark)
90 void benchmark()
91 {
92 	import std.meta : AliasSeq;
93 	import std.traits : isCopyable;
94 	alias Ts(uint n) = AliasSeq!(W!(byte, n), W!(ubyte, n),
95 								 W!(short, n), W!(ushort, n),
96 								 W!(int, n), W!(uint, n),
97 								 W!(long, n), W!(ulong, n),
98 								 W!(float, n), W!(cfloat, n),
99 								 W!(double, n), W!(cdouble, n),
100 								 W!(real, n), W!(creal, n),
101 								 W!(string, n), W!(wstring, n), W!(dstring, n));
102 
103 	enum n = 100;
104 	enum m = 100;
105 	static foreach (i; 0 .. n)
106 	{
107 		foreach (T; Ts!(n))
108 		{
109 			static foreach (j; 0 .. m)
110 			{
111 				static assert(maxSizeOf!(T) != 0);
112 			}
113 		}
114 	}
115 }