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 transformations.
7  *
8  * Templates in this module can be used to modify type qualifiers or transform
9  * types. They take some type as argument and return a different type after
10  * perfoming the specified transformation.
11  *
12  * Copyright: Eugene Wissner 2017-2020.
13  * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
14  *                  Mozilla Public License, v. 2.0).
15  * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
16  * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/meta/tanya/meta/transform.d,
17  *                 tanya/meta/transform.d)
18  */
19 module tanya.meta.transform;
20 
21 import tanya.meta.metafunction;
22 import tanya.meta.trait;
23 
24 /**
25  * Removes any type qualifiers from $(D_PARAM T).
26  *
27  * Removed qualifiers are:
28  * $(UL
29  *  $(LI const)
30  *  $(LI immutable)
31  *  $(LI inout)
32  *  $(LI shared)
33  * )
34  * and combinations of these.
35  *
36  * If the type $(D_PARAM T) doesn't have any qualifieres,
37  * $(D_INLINECODE Unqual!T) becomes an alias for $(D_PARAM T).
38  *
39  * Params:
40  *  T = A type.
41  *
42  * Returns: $(D_PARAM T) without any type qualifiers.
43  */
44 template Unqual(T)
45 {
46     static if (is(T U == shared const U)
47             || is(T U == shared inout U)
48             || is(T U == shared inout const U)
49             || is(T U == inout const U)
50             || is(T U == const U)
51             || is(T U == immutable U)
52             || is(T U == inout U)
53             || is(T U == shared U))
54     {
55         alias Unqual = U;
56     }
57     else
58     {
59         alias Unqual = T;
60     }
61 }
62 
63 ///
64 @nogc nothrow pure @safe unittest
65 {
66     static assert(is(Unqual!bool == bool));
67     static assert(is(Unqual!(immutable bool) == bool));
68     static assert(is(Unqual!(inout bool) == bool));
69     static assert(is(Unqual!(inout const bool) == bool));
70     static assert(is(Unqual!(shared bool) == bool));
71     static assert(is(Unqual!(shared const bool) == bool));
72     static assert(is(Unqual!(shared inout const bool) == bool));
73 }
74 
75 /**
76  * If $(D_PARAM T) is an $(D_KEYWORD enum), $(D_INLINECODE OriginalType!T)
77  * evaluates to the most base type of that $(D_KEYWORD enum) and to
78  * $(D_PARAM T) otherwise.
79  *
80  * Params:
81  *  T = A type.
82  *
83  * Returns: Base type of the $(D_KEYWORD enum) $(D_PARAM T) or $(D_PARAM T)
84  *          itself.
85  */
86 template OriginalType(T)
87 {
88     static if (is(T U == enum))
89     {
90         alias OriginalType = OriginalType!U;
91     }
92     else
93     {
94         alias OriginalType = T;
95     }
96 }
97 
98 ///
99 @nogc nothrow pure @safe unittest
100 {
101     enum E1 : const(int)
102     {
103         n = 0,
104     }
105     enum E2 : bool
106     {
107         t = true,
108     }
109     enum E3 : E2
110     {
111         t = E2.t,
112     }
113     enum E4 : const(E2)
114     {
115         t = E2.t,
116     }
117 
118     static assert(is(OriginalType!E1 == const int));
119     static assert(is(OriginalType!E2 == bool));
120     static assert(is(OriginalType!E3 == bool));
121     static assert(is(OriginalType!E4 == bool));
122     static assert(is(OriginalType!(const E4) == bool));
123 }
124 
125 /**
126  * Copies constness of $(D_PARAM From) to $(D_PARAM To).
127  *
128  * The following type qualifiers affect the constness and hence are copied:
129  * $(UL
130  *  $(LI const)
131  *  $(LI immutable)
132  *  $(LI inout)
133  *  $(LI inout const)
134  * )
135  *
136  * Params:
137  *  From = Source type.
138  *  To   = Target type.
139  *
140  * Returns: $(D_PARAM To) with the constness of $(D_PARAM From).
141  */
142 template CopyConstness(From, To)
143 {
144     static if (is(From T == immutable T))
145     {
146         alias CopyConstness = immutable To;
147     }
148     else static if (is(From T == const T) || is(From T == shared const T))
149     {
150         alias CopyConstness = const To;
151     }
152     else static if (is(From T == inout T) || is(From T == shared inout T))
153     {
154         alias CopyConstness = inout To;
155     }
156     else static if (is(From T == inout const T)
157                  || is(From T == shared inout const T))
158     {
159         alias CopyConstness = inout const To;
160     }
161     else
162     {
163         alias CopyConstness = To;
164     }
165 }
166 
167 ///
168 @nogc nothrow pure @safe unittest
169 {
170     static assert(is(CopyConstness!(int, char) == char));
171     static assert(is(CopyConstness!(const int, char) == const char));
172     static assert(is(CopyConstness!(immutable int, char) == immutable char));
173     static assert(is(CopyConstness!(inout int, char) == inout char));
174     static assert(is(CopyConstness!(inout const int, char) == inout const char));
175 
176     static assert(is(CopyConstness!(shared int, char) == char));
177     static assert(is(CopyConstness!(shared const int, char) == const char));
178     static assert(is(CopyConstness!(shared inout int, char) == inout char));
179     static assert(is(CopyConstness!(shared inout const int, char) == inout const char));
180 
181     static assert(is(CopyConstness!(const int, shared char) == shared const char));
182     static assert(is(CopyConstness!(const int, immutable char) == immutable char));
183     static assert(is(CopyConstness!(immutable int, const char) == immutable char));
184 }
185 
186 /**
187  * Retrieves the target type `U` of a pointer `U*`.
188  *
189  * Params:
190  *  T = Pointer type.
191  *
192  * Returns: Pointer target type.
193  */
194 template PointerTarget(T)
195 {
196     static if (is(T U : U*))
197     {
198         alias PointerTarget = U;
199     }
200     else
201     {
202         static assert(T.stringof ~ " isn't a pointer type");
203     }
204 }
205 
206 ///
207 @nogc nothrow pure @safe unittest
208 {
209     static assert(is(PointerTarget!(bool*) == bool));
210     static assert(is(PointerTarget!(const bool*) == const bool));
211     static assert(is(PointerTarget!(const shared bool*) == const shared bool));
212     static assert(!is(PointerTarget!bool));
213 }
214 
215 /**
216  * Params:
217  *  T = The type of the associative array.
218  *
219  * Returns: The key type of the associative array $(D_PARAM T).
220  */
221 template KeyType(T)
222 {
223     static if (is(T V : V[K], K))
224     {
225         alias KeyType = K;
226     }
227     else
228     {
229         static assert(false, T.stringof ~ " isn't an associative array");
230     }
231 }
232 
233 ///
234 @nogc nothrow pure @safe unittest
235 {
236     static assert(is(KeyType!(int[string]) == string));
237     static assert(!is(KeyType!(int[15])));
238 }
239 
240 /**
241  * Params:
242  *  T = The type of the associative array.
243  *
244  * Returns: The value type of the associative array $(D_PARAM T).
245  */
246 template ValueType(T)
247 {
248     static if (is(T V : V[K], K))
249     {
250         alias ValueType = V;
251     }
252     else
253     {
254         static assert(false, T.stringof ~ " isn't an associative array");
255     }
256 }
257 
258 ///
259 @nogc nothrow pure @safe unittest
260 {
261     static assert(is(ValueType!(int[string]) == int));
262     static assert(!is(ValueType!(int[15])));
263 }
264 
265 /**
266  * Adds $(D_KEYWORD inout) qualifier to the type $(D_PARAM T).
267  *
268  * Params:
269  *  T = A type.
270  *
271  * Returns: $(D_INLINECODE inout(T)).
272  */
273 alias InoutOf(T) = inout(T);
274 
275 ///
276 @nogc nothrow pure @safe unittest
277 {
278     static assert(is(InoutOf!int == inout int));
279 }
280 
281 /**
282  * Adds $(D_KEYWORD inout) qualifier to the type $(D_PARAM T).
283  *
284  * Params:
285  *  T = A type.
286  *
287  * Returns: $(D_INLINECODE inout(T)).
288  */
289 alias ConstOf(T) = const(T);
290 
291 ///
292 @nogc nothrow pure @safe unittest
293 {
294     static assert(is(ConstOf!int == const int));
295 }
296 
297 /**
298  * Adds $(D_KEYWORD inout) qualifier to the type $(D_PARAM T).
299  *
300  * Params:
301  *  T = A type.
302  *
303  * Returns: $(D_INLINECODE inout(T)).
304  */
305 alias SharedOf(T) = shared(T);
306 
307 ///
308 @nogc nothrow pure @safe unittest
309 {
310     static assert(is(SharedOf!int == shared int));
311 }
312 
313 /**
314  * Adds $(D_KEYWORD inout) qualifier to the type $(D_PARAM T).
315  *
316  * Params:
317  *  T = A type.
318  *
319  * Returns: $(D_INLINECODE inout(T)).
320  */
321 alias SharedInoutOf(T) = shared(inout T);
322 
323 ///
324 @nogc nothrow pure @safe unittest
325 {
326     static assert(is(SharedInoutOf!int == shared inout int));
327 }
328 
329 /**
330  * Adds $(D_KEYWORD shared const) qualifier to the type $(D_PARAM T).
331  *
332  * Params:
333  *  T = A type.
334  *
335  * Returns: $(D_INLINECODE shared(const T)).
336  */
337 alias SharedConstOf(T) = shared(const T);
338 
339 ///
340 @nogc nothrow pure @safe unittest
341 {
342     static assert(is(SharedConstOf!int == shared const int));
343 }
344 
345 /**
346  * Adds $(D_KEYWORD immutable) qualifier to the type $(D_PARAM T).
347  *
348  * Params:
349  *  T = A type.
350  *
351  * Returns: $(D_INLINECODE immutable(T)).
352  */
353 alias ImmutableOf(T) = immutable(T);
354 
355 ///
356 @nogc nothrow pure @safe unittest
357 {
358     static assert(is(ImmutableOf!int == immutable int));
359 }
360 
361 /**
362  * Adds $(D_KEYWORD inout const) qualifier to the type $(D_PARAM T).
363  *
364  * Params:
365  *  T = A type.
366  *
367  * Returns: $(D_INLINECODE inout(const T)).
368  */
369 alias InoutConstOf(T) = inout(const T);
370 
371 ///
372 @nogc nothrow pure @safe unittest
373 {
374     static assert(is(InoutConstOf!int == inout const int));
375 }
376 
377 /**
378  * Adds $(D_KEYWORD shared inout const) qualifier to the type $(D_PARAM T).
379  *
380  * Params:
381  *  T = A type.
382  *
383  * Returns: $(D_INLINECODE shared(inout const T)).
384  */
385 alias SharedInoutConstOf(T) = shared(inout const T);
386 
387 ///
388 @nogc nothrow pure @safe unittest
389 {
390     static assert(is(SharedInoutConstOf!int == shared inout const int));
391 }
392 
393 /**
394  * Determines the type of $(D_PARAM T). If $(D_PARAM T) is already a type,
395  * $(D_PSYMBOL TypeOf) aliases itself to $(D_PARAM T).
396  *
397  * $(D_PSYMBOL TypeOf) evaluates to $(D_KEYWORD void) for template arguments.
398  *
399  * The symbols that don't have a type and aren't types cannot be used as
400  * arguments to $(D_PSYMBOL TypeOf).
401  *
402  * Params:
403  *  T = Expression, type or template.
404  *
405  * Returns: The type of $(D_PARAM T).
406  */
407 alias TypeOf(T) = T;
408 
409 /// ditto
410 template TypeOf(alias T)
411 if (isExpressions!T || __traits(isTemplate, T))
412 {
413     alias TypeOf = typeof(T);
414 }
415 
416 ///
417 @nogc nothrow pure @safe unittest
418 {
419     struct S(T)
420     {
421     }
422     static assert(is(TypeOf!S == void));
423     static assert(is(TypeOf!int == int));
424     static assert(is(TypeOf!true == bool));
425     static assert(!is(TypeOf!(tanya.meta)));
426 }
427 
428 /**
429  * Finds the type with the smallest size in the $(D_PARAM Args) list. If
430  * several types have the same type, the leftmost is returned.
431  *
432  * Params:
433  *  Args = Type list.
434  *
435  * Returns: The smallest type.
436  *
437  * See_Also: $(D_PSYMBOL Largest).
438  */
439 template Smallest(Args...)
440 if (Args.length >= 1)
441 {
442     static assert(is(Args[0]), T.stringof ~ " doesn't have .sizeof property");
443 
444     static if (Args.length == 1)
445     {
446         alias Smallest = Args[0];
447     }
448     else static if (Smallest!(Args[1 .. $]).sizeof < Args[0].sizeof)
449     {
450         alias Smallest = Smallest!(Args[1 .. $]);
451     }
452     else
453     {
454         alias Smallest = Args[0];
455     }
456 }
457 
458 ///
459 @nogc nothrow pure @safe unittest
460 {
461     static assert(is(Smallest!(int, ushort, uint, short) == ushort));
462     static assert(is(Smallest!(short) == short));
463     static assert(is(Smallest!(ubyte[8], ubyte[5]) == ubyte[5]));
464     static assert(!is(Smallest!(short, 5)));
465 }
466 
467 /**
468  * Finds the type with the largest size in the $(D_PARAM Args) list. If several
469  * types have the same type, the leftmost is returned.
470  *
471  * Params:
472  *  Args = Type list.
473  *
474  * Returns: The largest type.
475  *
476  * See_Also: $(D_PSYMBOL Smallest).
477  */
478 template Largest(Args...)
479 if (Args.length >= 1)
480 {
481     static assert(is(Args[0]), T.stringof ~ " doesn't have .sizeof property");
482 
483     static if (Args.length == 1)
484     {
485         alias Largest = Args[0];
486     }
487     else static if (Largest!(Args[1 .. $]).sizeof > Args[0].sizeof)
488     {
489         alias Largest = Largest!(Args[1 .. $]);
490     }
491     else
492     {
493         alias Largest = Args[0];
494     }
495 }
496 
497 ///
498 @nogc nothrow pure @safe unittest
499 {
500     static assert(is(Largest!(int, short, uint) == int));
501     static assert(is(Largest!(short) == short));
502     static assert(is(Largest!(ubyte[8], ubyte[5]) == ubyte[8]));
503     static assert(!is(Largest!(short, 5)));
504 }