1 /**
2  * Several templates, alias, traits or functions related to types.
3  */
4 module iz.types;
5 
6 import
7     std.traits, std.meta;
8 
9 version(unittest) import std.stdio;
10 
11 /// pointer.
12 alias Ptr = void*;
13 
14 
15 /** 
16  * BasicTypes elements verify isBasicType().
17  */
18 alias BasicTypes = AliasSeq!( 
19     bool, byte, ubyte, short, ushort, int, uint, long, ulong, 
20     float, double, real, 
21     char, wchar, dchar
22 );
23 ///
24 static unittest
25 {
26     foreach(T; BasicTypes)
27         assert( isBasicType!T, T.stringof);
28 }
29 
30     
31 /**
32  * Returns true if T is a fixed-length data.
33  */
34 bool isFixedSize(T)()
35 {
36     return (
37         staticIndexOf!(T,BasicTypes) != -1) ||
38         (is(T==struct) & (__traits(isPOD, T))
39     );
40 }
41 
42 unittest
43 {
44     class Foo{}
45     struct Bar{byte a,b,c,d,e,f;}
46     alias myInt = int;
47     assert(isFixedSize!myInt);
48     assert(!isFixedSize!Foo);
49     assert(isFixedSize!Bar);
50 }
51 
52 /// Common type for all the delagate kinds, when seen as a struct (ptr & funcptr).
53 alias GenericDelegate = void delegate();
54 
55 /// Common type for all the function kinds.
56 alias GenericFunction = void function();
57 
58 
59 /**
60  * Returns the dynamic class name of an Object or an interface.
61  * Params:
62  *      assumeDemangled = Must only be set to false if the class is declared in a unittest.
63  *      t = Either an interface or a class instance.
64  */
65 string className(bool assumeDemangled = true, T)(T t)
66 if (is(T == class) || is(T == interface))
67 {
68     static if (is(T == class)) Object o = t;
69     else Object o = cast(Object) t;
70     import std.array: split;
71     static if (assumeDemangled)
72         return (cast(TypeInfo_Class)typeid(o)).name.split('.')[$-1];
73     else
74     {
75         import std.demangle: demangle;
76         return (cast(TypeInfo_Class)typeid(o)).name.demangle.split('.')[$-1];
77     }
78 }
79 ///
80 unittest
81 {
82     static interface I {}
83     static class A{}
84     static class B: I{}
85     static class C{}
86     assert(className(new A) == "A");
87     assert(className(new B) == "B");
88     assert(className(cast(Object)new A) == "A");
89     assert(className(cast(Object)new B) == "B");
90     assert(className(cast(I) new B) == "B");
91     assert(className!(false)(new C) == "C");
92 }
93 
94 /**
95  * Indicates if the member of a struct or class is accessible for compile time
96  * introspection.
97  *
98  * The template has to be mixed in the scope where the other __traits()
99  * operations are performed.
100  * A simple function template that uses __traits(getProtection) does not faithfully
101  * represent the member accessibility if the function is declared in another module.
102  * Another problem is that __traits(getProtection) does not well represent the
103  * accessibility of the private members (own members or friend classes /structs).
104  */
105 mixin template ScopedReachability()
106 {
107     bool isMemberReachable(T, string member)()
108     if (is(T==class) || is(T==struct) || is(T==union))
109     {
110         return is(typeof(__traits(getMember, T, member)));
111     }
112 }
113 
114 /**
115  * Detects whether type $(D T) is a multi dimensional array.
116  *
117  * Params:
118  *      T = type to be tested
119  *
120  * Returns:
121  *      true if T is a multi dimensional array
122  */
123 template isMultiDimensionalArray(T)
124 {
125     static if (!isArray!T)
126         enum isMultiDimensionalArray = false;
127     else
128     {
129         import std.range.primitives: hasLength;
130         alias DT = typeof(T.init[0]);
131         enum isMultiDimensionalArray = hasLength!DT;
132     }
133 }
134 ///
135 unittest
136 {
137     static assert(!isMultiDimensionalArray!(string[]) );
138     static assert(isMultiDimensionalArray!(int[][]) );
139     static assert(!isMultiDimensionalArray!(int[]) );
140     static assert(!isMultiDimensionalArray!(int) );
141     static assert(!isMultiDimensionalArray!(string) );
142     static assert(!isMultiDimensionalArray!(int[][] function()) );
143     static assert(!isMultiDimensionalArray!void);
144 }
145 
146 /**
147  * Indicates the dimension count of an built-in array.
148  *
149  * Params:
150  *      T = type to be tested
151  *
152  * Returns:
153  *      0 if $(D T) is an not a build-in array, otherwise a number
154  *      at least equal to 1, according to the array dimension count.
155  */
156 template dimensionCount(T)
157 {
158     static if (isArray!T)
159     {
160         static if (isMultiDimensionalArray!T)
161         {
162             alias DT = typeof(T.init[0]);
163             enum dimensionCount = dimensionCount!DT + 1;
164         }
165         else enum dimensionCount = 1;
166     }
167     else enum dimensionCount = 0;
168 }
169 ///
170 unittest
171 {
172     static assert(dimensionCount!char == 0);
173     static assert(dimensionCount!(string[]) == 1);
174     static assert(dimensionCount!(int[]) == 1);
175     static assert(dimensionCount!(int[][]) == 2);
176     static assert(dimensionCount!(int[][][]) == 3);
177 }
178 
179 /**
180  * Indicates the array element type of an array.
181  *
182  * Contrary to $(D ElementType), dchar is not returned for narrow strings.
183  * The template strips the type lookup goes until the last dimenssion of a
184  * multi-dim array.
185  *
186  * Params:
187  *      T = type to be tested.
188  *
189  * Returns:
190  *      T element type.
191  */
192 template ArrayElementType(T)
193 if (isArray!T)
194 {
195     static if (isArray!(typeof(T.init[0])))
196         alias ArrayElementType = ArrayElementType!(typeof(T.init[0]));
197     else
198         alias ArrayElementType = typeof(T.init[0]);
199 }
200 ///
201 unittest
202 {
203     static assert(is(ArrayElementType!(int[][]) == int));
204     static assert(is(ArrayElementType!(char[][][][][]) == char));
205     static assert(is(ArrayElementType!(wchar[]) == wchar));
206 }
207 
208 /**
209  * Indicates wether an enum is ordered.
210  *
211  * An enum is considered as ordered if its members can index an array,
212  * optionally with a start offset.
213  *
214  * Params:
215  *      T = enum to be tested.
216  *
217  * Returns:
218  *      true if T members type is integral and if the delta between each member
219  *      is one, false otherwise.
220  */
221 template isOrderedEnum(T)
222 if (is(T == enum))
223 {
224     static if (!isIntegral!(OriginalType!T))
225     {
226         enum isOrderedEnum = false;
227     }
228     else
229     {
230         bool checker()
231         {
232             OriginalType!T previous = T.min;
233             foreach(member; EnumMembers!T[1..$])
234             {
235                 if (member != previous + 1)
236                     return false;
237                 previous = member;
238             }
239             return true;
240         }
241         enum isOrderedEnum = checker;
242     }
243 }
244 ///
245 unittest
246 {
247     enum A {a,z,e,r}
248     static assert(isOrderedEnum!A);
249     enum B: ubyte {a = 2,z,e,r}
250     static assert(isOrderedEnum!B);
251 
252     enum C: float {a,z,e,r}
253     static assert(!isOrderedEnum!C);
254     enum D: uint {a,z = 8,e,r}
255     static assert(!isOrderedEnum!D);
256 }
257 
258 /**
259  * Returns a pointer to the default constructor of a class
260  */
261 auto defaultConstructor(C)()
262 if (is(C == class))
263 {
264     alias CtorT = C function();
265     static if (__traits(hasMember, C, "__ctor"))
266     foreach(overload; __traits(getOverloads, C, "__ctor"))
267     {
268         static if (is(Unqual!(ReturnType!overload) == C) && !Parameters!overload.length)
269             return &overload;
270     }
271 }
272 
273 /**
274  * Indicates wether a class has a default constructor.
275  */
276 template hasDefaultConstructor(C)
277 if(is(C==class))
278 {
279     enum hasDefaultConstructor = !is(typeof(defaultConstructor!C()) == void);
280 }
281 ///
282 unittest
283 {
284     class A{}
285     class B{this() const {}}
286     class C{this(int a){}}
287     class D{this(){} this(int a){}}
288 
289     static assert(!hasDefaultConstructor!A);
290     static assert( hasDefaultConstructor!B);
291     static assert(!hasDefaultConstructor!C);
292     static assert( hasDefaultConstructor!D);
293 }
294 
295 /**
296  * Indicates wether an aggregate can be used with the $(D in) operator.
297  *
298  * Params:
299  *      T = The aggregate or an associative array type.
300  *      A = The type of the $(D in) left hand side argument.
301  */
302 template hasInOperator(T, A)
303 {
304     static if (isAssociativeArray!T && is(KeyType!T == A))
305         enum hasInOperator = true;
306     else static if (is(T == class) || is(T == struct))
307     {
308         static if (is(typeof({A a; auto b = a in T;})))
309             enum hasInOperator = true;
310         else
311         {
312             static if (is(typeof({A a; auto b = a in new T;})))
313                 enum hasInOperator = true;
314             else
315                 enum hasInOperator = false;
316         }
317     }
318     else
319         enum hasInOperator = false;
320 }
321 ///
322 unittest
323 {
324     alias AA = int[string];
325     static assert(hasInOperator!(AA, string));
326     static assert(!hasInOperator!(AA, int));
327 
328     struct Tester1 {static bool opIn_r(int){return true;}}
329     static assert(hasInOperator!(Tester1, int));
330 
331     struct Tester2 {bool opIn_r(int){return true;}}
332     static assert(hasInOperator!(Tester2, int));
333     static assert(!hasInOperator!(Tester2, string));
334 
335     struct Nothing {}
336     static assert(!hasInOperator!(Nothing, int));
337 }
338 
339 /**
340  * Detects wether T is an instantiated template.
341  */
342 template isTemplateInstance(alias T : Base!Args, alias Base, Args...)
343 {
344     enum isTemplateInstance = is(typeof(T));
345 }
346 ///
347 unittest
348 {
349     enum a(T) = false;
350     void b(T)(int){}
351     void c()(){}
352     template d(T){}
353     class e(T){T t;}
354     interface I{}
355 
356     static assert(isTemplateInstance!(a!int));
357     static assert(isTemplateInstance!(b!int));
358     static assert(isTemplateInstance!(d!int));
359     static assert(isTemplateInstance!(e!int));
360 
361     static assert(!isTemplateInstance!(I));
362     static assert(!isTemplateInstance!(int));
363 
364     static assert(!isTemplateInstance!(a));
365     static assert(!isTemplateInstance!(b));
366     static assert(!isTemplateInstance!(c));
367 }
368 
369 /// ditto
370 template isTemplateInstance(T : Base!Args, alias Base, Args...)
371 {
372     enum isTemplateInstance = is(T);
373 }
374 
375 /// ditto
376 template isTemplateInstance(T)
377 {
378     enum isTemplateInstance = false;
379 }
380 
381 /// ditto
382 template isTemplateInstance(alias T)
383 {
384     enum isTemplateInstance = isTemplateInstance!(typeof(T));
385 }
386 
387 template TemplateBase(T : Base!Args, alias Base, Args...)
388 if (is(T == class) || is(T == interface) || is(T == struct) || is(T == union))
389 {
390     alias TemplateBase = Base;
391 }
392 
393 unittest
394 {
395     class A(T){}
396     template B(T){class R{}}
397     alias F = B!int.R;
398 }
399 
400 /**
401  * Indicates wether a type or a variable type is an eponymous template.
402  */
403 template isEponymousTemplate(T)
404 {
405     static if (is(T == class) || is(T == interface) || is(T == struct) || is(T == union))
406     {
407         enum p = __traits(parent, T).stringof == T.stringof;
408         enum isEponymousTemplate = p && isTemplateInstance!T;
409     }
410     else
411         enum isEponymousTemplate = false;
412 }
413 ///
414 unittest
415 {
416     static class A(T){}
417     static struct B(T){}
418     static assert(isEponymousTemplate!(A!int));
419     static assert(isEponymousTemplate!(B!int));
420     static assert(!isEponymousTemplate!int);
421     template C(T)
422     {
423         static class C{}
424     }
425     static assert(isEponymousTemplate!(C!int));
426 }
427 
428 unittest
429 {
430     template A(T)
431     {
432         static class A{}
433     }
434     static assert(isEponymousTemplate!(A!int));
435 
436     template B(T)
437     {
438         static class A{}
439     }
440     static assert(!isEponymousTemplate!(B!int.A));
441 
442     class C(T){}
443     static assert(!isEponymousTemplate!int);
444 
445     A!int a;
446     static assert(isEponymousTemplate!a);
447 }
448 
449 
450 /// ditto
451 template isEponymousTemplate(alias T)
452 {
453     static if (is(typeof(T)))
454         enum isEponymousTemplate = isEponymousTemplate!(typeof(T));
455     else
456         enum isEponymousTemplate = false;
457 }
458 
459 /**
460  * Decomposes the chain of the templates and arguments used to create the alias
461  * passed as argument.
462  *
463  * Params:
464  *      T = An alias.
465  *      TemplateAndArgsOf = used internally only.
466  *
467  * Returns:
468  *      An AliasSeq formed by a chain of templates and arguments.
469  */
470 template NestedTemplateAndArgsOf(alias T, TemplateAndArgsOf...)
471 {
472     static if (isTemplateInstance!(AliasSeq!(__traits(parent, T))[0]))
473         alias PT = AliasSeq!(__traits(parent, T));
474     else
475         alias PT = void;
476 
477     static if (!is(PT == void))
478     {
479         alias NestedTemplateAndArgsOf = AliasSeq!(NestedTemplateAndArgsOf!
480             (__traits(parent, T)), TemplateOf!T, TemplateArgsOf!T
481         );
482     }
483     else static if (isTemplateInstance!T)
484     {
485         alias NestedTemplateAndArgsOf = AliasSeq!(TemplateOf!T, TemplateArgsOf!T,
486             TemplateAndArgsOf);
487     }
488     else alias NestedTemplateAndArgsOf = void;
489 }
490 ///
491 unittest
492 {
493     template A(As...)
494     {
495         template B(Bs...){int a;}
496     }
497     alias AB0 = A!(1,2).B!(3,4,5);
498     alias SQ0 = NestedTemplateAndArgsOf!AB0;
499     static assert(__traits(isSame, SQ0[0], A));
500     static assert(__traits(isSame, SQ0[1], 1));
501     static assert(__traits(isSame, SQ0[2], 2));
502     static assert(__traits(isSame, SQ0[3], A!(1,2).B));
503     static assert(__traits(isSame, SQ0[4], 3));
504     static assert(__traits(isSame, SQ0[5], 4));
505     static assert(__traits(isSame, SQ0[6], 5));
506 
507     template C(T)
508     {
509         template D(T)
510         {
511             template E(T) {}
512         }
513     }
514     alias CDE0 = C!int.D!int.E!int;
515     alias SQ1 = NestedTemplateAndArgsOf!CDE0;
516     static assert(__traits(isSame, SQ1[0], C));
517     static assert(is(SQ1[1] == int));
518     static assert(__traits(isSame, SQ1[2], C!int.D));
519     static assert(is(SQ1[3] == int));
520     static assert(__traits(isSame, SQ1[4], C!int.D!int.E));
521     static assert(is(SQ1[5] == int));
522 
523     alias B = NestedTemplateAndArgsOf!int;
524     static assert(is(B == void));
525 
526     template G(Gs...)
527     {
528         struct G {}
529     }
530 
531     alias GI = G!int;
532 
533     //pragma(msg, NestedTemplateAndArgsOf!(G!int).stringof);
534 }
535 
536 /// ditto
537 template NestedTemplateAndArgsOf(T)
538 {
539     static if (__traits(isTemplate, T))
540     {
541         alias TT = TemplateOf!T;
542         alias NestedTemplateAndArgsOf = NestedTemplateAndArgsOf!TT;
543     }
544     else static if (isEponymousTemplate!T)
545     {
546         alias TT = TemplateBase!T;
547         alias NestedTemplateAndArgsOf = NestedTemplateAndArgsOf!TT;
548     }
549     else alias NestedTemplateAndArgsOf = void;
550 }
551 
552 /**
553  * Indicates wether something is a specialized type.
554  *
555  * Returns:
556  * If the input argument is a type or a variable declaration for a type that's
557  * an complete specialization of a template then true is returned. In all the
558  * other cases (build-in types, non templatized aggregates or aliases to
559  * partially specialized templates, false.
560  */
561 template isSpecializedType(T)
562 {
563     enum isSpecializedType = isTemplateInstance!T && is(T);
564 }
565 ///
566 unittest
567 {
568     class A(T){}
569     static assert(isSpecializedType!(A!int));
570     class B(T,TT){}
571     alias PartialB(T) = B!int;
572     static assert(!isSpecializedType!PartialB);
573     static assert(isSpecializedType!(B!(int,void)));
574     static assert(!isSpecializedType!int);
575 }
576 
577 /// ditto
578 template isSpecializedType(alias T)
579 {
580     static if (is(typeof(T)))
581         enum isSpecializedType = isSpecializedType!(typeof(T));
582     else
583         enum isSpecializedType = false;
584 }
585 
586 /**
587  * Indicates wether something is a value known at compile time.
588  *
589  * Params:
590  *      V = The value to test.
591  *      T = Optional, the expected value type.
592  */
593 template isCompileTimeValue(alias V, T...)
594 if (T.length == 0 || (T.length == 1 && is(T[0])))
595 {
596     enum isKnown = is(typeof((){enum v = V;}));
597     static if (!T.length)
598         enum isCompileTimeValue = isKnown;
599     else
600         enum isCompileTimeValue = isKnown && is(typeof(V) == T[0]);
601 }
602 ///
603 unittest
604 {
605     string a;
606     enum b = "0";
607     enum c = 0;
608     static assert(!isCompileTimeValue!a);
609     static assert(isCompileTimeValue!b);
610     static assert(isCompileTimeValue!c);
611     static assert(isCompileTimeValue!(b,string));
612     static assert(isCompileTimeValue!(c,int));
613     static assert(!isCompileTimeValue!(c,char));
614     static assert(!isCompileTimeValue!(char));
615 }
616 
617 /// ditto
618 template isCompileTimeValue(V, T...)
619 if (T.length == 0 || (T.length == 1 && is(T[0])))
620 {
621     enum isCompileTimeValue = false;
622 }
623 
624 /**
625  * Indicates wether something is a string literal.
626  */
627 enum isStringLiteral(alias V) = isCompileTimeValue!(V, string);
628 
629 /// ditto
630 template isStringLiteral(V){enum isStringLiteral = false;}
631 
632 ///
633 unittest
634 {
635     string a;
636     enum b = "0";
637     enum c = 0;
638     static assert(!isStringLiteral!a);
639     static assert(isStringLiteral!b);
640     static assert(!isStringLiteral!c);
641     static assert(!isStringLiteral!int);
642 }
643 
644 /**
645  * Indicates wether the class passed as template argument has the extern(C++)
646  * linkage attribute.
647  */
648 template isCppClass(T)
649 if (is(T == class))
650 {
651     bool allCpp()
652     {
653         bool result = true;
654         foreach (member; __traits(allMembers, T))
655         {
656             static if (member != "this") // ?
657             static if (__traits(getOverloads, T, member).length)
658             foreach (ov; __traits(getOverloads, T, member))
659                 static if (functionLinkage!ov != "C++")
660             {
661                 result = false;
662                 break;
663             }
664             return result;
665         }
666     }
667     enum isCppClass = allCpp();
668 }
669 ///
670 unittest
671 {
672     class Foo{void bar(){} int a;}
673     static assert(!isCppClass!Foo);
674 
675     extern(C++) class Bar{void bar(){} int a;}
676     static assert(isCppClass!Bar);
677 }
678 
679 /**
680  * Indicates wether the type passed as template parameter has a custom
681  * $(D opEquals) function that allows self comparison.
682  *
683  * This triat mostly allows compare objects, with a known derived type, in
684  * $(D @nogc) code.
685  */
686 template hasElaborateSelfEquals(T)
687 {
688     static if (is(T == class) || is(T == struct))
689     {
690         static if (is(T == class))
691             alias B = Object;
692         else
693             alias B = Unqual!T;
694         static if (__traits(hasMember, T, "opEquals")
695             && Parameters!(T.opEquals).length == 1
696             && is(Unqual!(Parameters!(T.opEquals)[0]) : B))
697             enum bool hasElaborateSelfEquals = true;
698         else
699             enum bool hasElaborateSelfEquals = false;
700     }
701     else enum bool hasElaborateSelfEquals = false;
702 }
703 ///
704 unittest
705 {
706     struct Foo {}
707     struct Bar {bool opEquals(const(Bar)){return true;}}
708     struct Baz {bool opEquals(Foo){return true;}}
709     static assert(!hasElaborateSelfEquals!Foo);
710     static assert(hasElaborateSelfEquals!Bar);
711     static assert(!hasElaborateSelfEquals!Baz);
712 }
713