1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 /**
6  * Type constructors.
7  *
8  * This module contains templates that allow to build new types from the
9  * available ones.
10  *
11  * Copyright: Eugene Wissner 2017-2020.
12  * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
13  *                  Mozilla Public License, v. 2.0).
14  * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
15  * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/typecons.d,
16  *                 tanya/typecons.d)
17  */
18 module tanya.typecons;
19 
20 import tanya.format;
21 import tanya.memory.lifetime;
22 import tanya.meta.metafunction;
23 import tanya.meta.trait;
24 
25 /**
26  * $(D_PSYMBOL Tuple) can store two or more heterogeneous objects.
27  *
28  * The objects can by accessed by index as `obj[0]` and `obj[1]` or by optional
29  * names (e.g. `obj.first`).
30  *
31  * $(D_PARAM Specs) contains a list of object types and names. First
32  * comes the object type, then an optional string containing the name.
33  * If you want the object be accessible only by its index (`0` or `1`),
34  * just skip the name.
35  *
36  * Params:
37  *  Specs = Field types and names.
38  *
39  * See_Also: $(D_PSYMBOL tuple).
40  */
41 template Tuple(Specs...)
42 {
43     template parseSpecs(size_t fieldCount, Specs...)
44     {
45         static if (Specs.length == 0)
46         {
47             alias parseSpecs = AliasSeq!();
48         }
49         else static if (is(Specs[0]) && fieldCount < 2)
50         {
51             static if (is(typeof(Specs[1]) == string))
52             {
53                 alias parseSpecs
54                     = AliasSeq!(Pack!(Specs[0], Specs[1]),
55                                 parseSpecs!(fieldCount + 1, Specs[2 .. $]));
56             }
57             else
58             {
59                 alias parseSpecs
60                     = AliasSeq!(Pack!(Specs[0]),
61                                 parseSpecs!(fieldCount + 1, Specs[1 .. $]));
62             }
63         }
64         else
65         {
66             static assert(false, "Invalid argument: " ~ Specs[0].stringof);
67         }
68     }
69 
70     alias ChooseType(alias T) = T.Seq[0];
71     alias ParsedSpecs = parseSpecs!(0, Specs);
72 
73     static assert(ParsedSpecs.length > 1, "Invalid argument count");
74 
75     private string formatAliases(size_t n, Specs...)()
76     {
77         static if (Specs.length == 0)
78         {
79             return "";
80         }
81         else
82         {
83             string fieldAlias;
84             static if (Specs[0].length == 2)
85             {
86                 char[21] buffer;
87                 fieldAlias = "alias " ~ Specs[0][1] ~ " = expand["
88                            ~ integral2String(n, buffer).idup ~ "];";
89             }
90             return fieldAlias ~ formatAliases!(n + 1, Specs[1 .. $])();
91         }
92     }
93 
94     struct Tuple
95     {
96         /// Field types.
97         alias Types = Map!(ChooseType, ParsedSpecs);
98 
99         // Create field aliases.
100         mixin(formatAliases!(0, ParsedSpecs[0 .. $])());
101 
102         /// Represents the values of the $(D_PSYMBOL Tuple) as a list of values.
103         Types expand;
104 
105         alias expand this;
106     }
107 }
108 
109 ///
110 @nogc nothrow pure @safe unittest
111 {
112     auto pair = Tuple!(int, "first", string, "second")(1, "second");
113     assert(pair.first == 1);
114     assert(pair[0] == 1);
115     assert(pair.second == "second");
116     assert(pair[1] == "second");
117 }
118 
119 /**
120  * Creates a new $(D_PSYMBOL Tuple).
121  *
122  * Params:
123  *  Names = Field names.
124  *
125  * See_Also: $(D_PSYMBOL Tuple).
126  */
127 template tuple(Names...)
128 {
129     /**
130      * Creates a new $(D_PSYMBOL Tuple).
131      *
132      * Params:
133      *  Args = Field types.
134      *  args = Field values.
135      *
136      * Returns: Newly created $(D_PSYMBOL Tuple).
137      */
138     auto tuple(Args...)(auto ref Args args)
139     if (Args.length >= Names.length && isTypeTuple!Args)
140     {
141         alias Zipped = ZipWith!(AliasSeq, Pack!Args, Pack!Names);
142         alias Nameless = Args[Names.length .. $];
143 
144         return Tuple!(Zipped, Nameless)(forward!args);
145     }
146 }
147 
148 ///
149 @nogc nothrow pure @safe unittest
150 {
151     auto t = tuple!("one", "two")(20, 5);
152     assert(t.one == 20);
153     assert(t.two == 5);
154 }
155 
156 /**
157  * Type that can hold one of the types listed as its template parameters.
158  *
159  * $(D_PSYMBOL Variant) is a type similar to $(D_KEYWORD union), but
160  * $(D_PSYMBOL Variant) keeps track of the actually used type and throws an
161  * assertion error when trying to access an invalid type at runtime.
162  *
163  * Params:
164  *  Specs = Types this $(D_SPYBMOL Variant) can hold.
165  */
166 template Variant(Specs...)
167 if (isTypeTuple!Specs && NoDuplicates!Specs.length == Specs.length)
168 {
169     union AlignedUnion(Args...)
170     {
171         static if (Args.length > 0)
172         {
173             Args[0] value;
174         }
175         static if (Args.length > 1)
176         {
177             AlignedUnion!(Args[1 .. $]) rest;
178         }
179     }
180 
181     private struct VariantAccessorInfo
182     {
183         string accessor;
184         ptrdiff_t tag;
185     }
186 
187     template accessor(T, Union)
188     {
189         enum VariantAccessorInfo info = accessorImpl!(T, Union, 1);
190         enum accessor = VariantAccessorInfo("this.values" ~ info.accessor, info.tag);
191     }
192 
193     template accessorImpl(T, Union, size_t tag)
194     {
195         static if (is(T == typeof(Union.value)))
196         {
197             enum accessorImpl = VariantAccessorInfo(".value", tag);
198         }
199         else
200         {
201             enum VariantAccessorInfo info = accessorImpl!(T, typeof(Union.rest), tag + 1);
202             enum accessorImpl = VariantAccessorInfo(".rest" ~ info.accessor, info.tag);
203         }
204     }
205 
206     struct Variant
207     {
208         /// Types can be present in this $(D_PSYMBOL Variant).
209         alias Types = Specs;
210 
211         private ptrdiff_t tag = -1;
212         private AlignedUnion!Types values;
213 
214         /**
215          * Constructs this $(D_PSYMBOL Variant) with one of the types supported
216          * in it.
217          *
218          * Params:
219          *  T     = Type of the initial value.
220          *  value = Initial value.
221          */
222         this(T)(ref T value)
223         if (canFind!(T, Types))
224         {
225             copyAssign!T(value);
226         }
227 
228         /// ditto
229         this(T)(T value)
230         if (canFind!(T, Types))
231         {
232             moveAssign!T(value);
233         }
234 
235         ~this()
236         {
237             reset();
238         }
239 
240         this(this)
241         {
242             alias pred(U) = hasElaborateCopyConstructor!(U.Seq[1]);
243             static foreach (Type; Filter!(pred, Enumerate!Types))
244             {
245                 if (this.tag == Type.Seq[0])
246                 {
247                     get!(Type.Seq[1]).__postblit();
248                 }
249             }
250         }
251 
252         /**
253          * Tells whether this $(D_PSYMBOL Variant) is initialized.
254          *
255          * Returns: $(D_KEYWORD true) if this $(D_PSYMBOL Variant) contains a
256          *          value, $(D_KEYWORD false) otherwise.
257          */
258         bool hasValue() const
259         {
260             return this.tag != -1;
261         }
262 
263         /**
264          * Tells whether this $(D_PSYMBOL Variant) holds currently a value of
265          * type $(D_PARAM T).
266          *
267          * Params:
268          *  T = Examined type.
269          *
270          * Returns: $(D_KEYWORD true) if this $(D_PSYMBOL Variant) currently
271          *          contains a value of type $(D_PARAM T), $(D_KEYWORD false)
272          *          otherwise.
273          */
274         bool peek(T)() const
275         if (canFind!(T, Types))
276         {
277             return this.tag == staticIndexOf!(T, Types);
278         }
279 
280         /**
281          * Returns the underlying value, assuming it is of the type $(D_PARAM T).
282          *
283          * Params:
284          *  T = Type of the value should be returned.
285          *
286          * Returns: The underyling value.
287          *
288          * Precondition: The $(D_PSYMBOL Variant) has a value.
289          *
290          * See_Also: $(D_PSYMBOL peek), $(D_PSYMBOL hasValue).
291          */
292         ref inout(T) get(T)() inout
293         if (canFind!(T, Types))
294         in
295         {
296             assert(this.tag == staticIndexOf!(T, Types), "Variant isn't initialized");
297         }
298         do
299         {
300             mixin("return " ~ accessor!(T, AlignedUnion!Types).accessor ~ ";");
301         }
302 
303         /**
304          * Reassigns the value.
305          *
306          * Params:
307          *  T    = Type of the new value
308          *  that = New value.
309          *
310          * Returns: $(D_KEYWORD this).
311          */
312         ref typeof(this) opAssign(T)(T that)
313         if (canFind!(T, Types))
314         {
315             reset();
316             return moveAssign!T(that);
317         }
318 
319         /// ditto
320         ref typeof(this) opAssign(T)(ref T that)
321         if (canFind!(T, Types))
322         {
323             reset();
324             return copyAssign!T(that);
325         }
326 
327         private ref typeof(this) moveAssign(T)(ref T that) @trusted
328         {
329             this.tag = staticIndexOf!(T, Types);
330 
331             enum string accessorMixin = accessor!(T, AlignedUnion!Types).accessor;
332             moveEmplace(that, mixin(accessorMixin));
333 
334             return this;
335         }
336 
337         private ref typeof(this) copyAssign(T)(ref T that) return
338         {
339             this.tag = staticIndexOf!(T, Types);
340 
341             enum string accessorMixin = accessor!(T, AlignedUnion!Types).accessor;
342             emplace!T((() @trusted => (&mixin(accessorMixin))[0 .. 1])(), that);
343 
344             return this;
345         }
346 
347         private void reset()
348         {
349             alias pred(U) = hasElaborateDestructor!(U.Seq[1]);
350             static foreach (Type; Filter!(pred, Enumerate!Types))
351             {
352                 if (this.tag == Type.Seq[0])
353                 {
354                     destroy(get!(Type.Seq[1]));
355                 }
356             }
357         }
358 
359         /**
360          * Returns $(D_PSYMBOL TypeInfo) corresponding to the current type.
361          *
362          * If this $(D_PSYMBOL Variant) isn't initialized, returns
363          * $(D_KEYWORD null).
364          *
365          * Returns: $(D_PSYMBOL TypeInfo) of the current type.
366          */
367         @property TypeInfo type()
368         {
369             static foreach (i, Type; Types)
370             {
371                 if (this.tag == i)
372                 {
373                     return typeid(Type);
374                 }
375             }
376             return null;
377         }
378 
379         /**
380          * Compares this $(D_PSYMBOL Variant) with another one with the same
381          * specification for equality.
382          *
383          * $(UL
384          *  $(LI If both hold values of the same type, these values are
385          *       compared.)
386          *  $(LI If they hold values of different types, then the
387          *       $(D_PSYMBOL Variant)s aren't equal.)
388          *  $(LI If only one of them is initialized but another one not, they
389          *       aren't equal.)
390          *  $(LI If neither of them is initialized, they are equal.)
391          * )
392          *
393          * Params:
394          *  that = The $(D_PSYMBOL Variant) to compare with.
395          *
396          * Returns: $(D_KEYWORD true) if this $(D_PSYMBOL Variant) is equal to
397          *          $(D_PARAM that), $(D_KEYWORD false) otherwise.
398          */
399         bool opEquals()(auto ref inout(Variant) that) inout
400         {
401             if (this.tag != that.tag)
402             {
403                 return false;
404             }
405             static foreach (i, Type; Types)
406             {
407                 if (this.tag == i)
408                 {
409                     return get!Type == that.get!Type;
410                 }
411             }
412             return true;
413         }
414     }
415 }
416 
417 ///
418 @nogc nothrow pure @safe unittest
419 {
420     Variant!(int, double) variant = 5;
421     assert(variant.peek!int);
422     assert(variant.get!int == 5);
423 
424     variant = 5.4;
425     assert(!variant.peek!int);
426     assert(variant.get!double == 5.4);
427 }