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 = 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 @safe pure unittest
23 {
24     static assert(maxSizeOf!(char) == 1);
25     static assert(maxSizeOf!(byte) == 1);
26     static assert(maxSizeOf!(byte, short) == 2);
27     static assert(maxSizeOf!(short, byte) == 2);
28     static assert(maxSizeOf!(byte, short, int) == 4);
29     static assert(maxSizeOf!(byte, short, int, long) == 8);
30     static assert(maxSizeOf!(byte, short, int, string) == 16);
31     static assert(!__traits(compiles, { enum _ = maxSizeOf!(byte, void); }));
32 }
33 
34 // alternative implementation that supports `void`
35 static template maxSizeOf_1(Ts...)
36 {
37     align(1) union Impl {
38         static foreach (i, T; Ts) {
39             static if (!is(T == void))
40                 mixin("T _field_" ~ i.stringof ~ ";");
41         }
42     }
43     enum maxSizeOf_1 = Impl.sizeof;
44 }
45 
46 ///
47 @safe pure unittest
48 {
49     static assert(maxSizeOf_1!(char) == 1);
50     static assert(maxSizeOf_1!(byte) == 1);
51     static assert(maxSizeOf_1!(byte, short) == 2);
52     static assert(maxSizeOf_1!(short, byte) == 2);
53     static assert(maxSizeOf_1!(byte, short, int) == 4);
54     static assert(maxSizeOf_1!(byte, short, int, long) == 8);
55     static assert(maxSizeOf_1!(byte, short, int, string) == 16);
56     static assert(maxSizeOf_1!(byte, void) == 1);
57     static assert(maxSizeOf_1!(byte, short, void) == 2);
58 }
59 
60 template maxSizeOf_2(Ts...)
61 {
62     enum maxSizeOf_2 = compute();
63     auto compute()
64     {
65         size_t result;
66         static foreach (T; Ts)
67             if (T.sizeof > result)
68                 result = T.sizeof;
69         return result;
70     }
71 }
72 
73 ///
74 @safe pure unittest
75 {
76     static assert(maxSizeOf_2!(char) == 1);
77     static assert(maxSizeOf_2!(byte) == 1);
78     static assert(maxSizeOf_2!(byte, short) == 2);
79     static assert(maxSizeOf_2!(short, byte) == 2);
80     static assert(maxSizeOf_2!(byte, short, int) == 4);
81     static assert(maxSizeOf_2!(byte, short, int, long) == 8);
82     static assert(maxSizeOf_2!(byte, short, int, string) == 16);
83     static assert(maxSizeOf_2!(byte, void) == 1);
84     static assert(maxSizeOf_2!(byte, short, void) == 2);
85 }
86 
87 struct W(T, size_t n)
88 {
89     T value;
90 }
91 
92 version(benchmark)
93 void benchmark()
94 {
95     import std.meta : AliasSeq;
96     import std.traits : isCopyable;
97     alias Ts(uint n) = AliasSeq!(W!(byte, n), W!(ubyte, n),
98                                  W!(short, n), W!(ushort, n),
99                                  W!(int, n), W!(uint, n),
100                                  W!(long, n), W!(ulong, n),
101                                  W!(float, n), W!(cfloat, n),
102                                  W!(double, n), W!(cdouble, n),
103                                  W!(real, n), W!(creal, n),
104                                  W!(string, n), W!(wstring, n), W!(dstring, n));
105 
106     enum n = 100;
107     enum m = 100;
108     static foreach (i; 0 .. n)
109     {
110         foreach (T; Ts!(n))
111         {
112             static foreach (j; 0 .. m)
113             {
114                 static assert(maxSizeOf!(T) != 0);
115             }
116         }
117     }
118 }