1 /**
2  * The traits module defines tools useful for obtaining detailed compile-time
3  * information about a type.  Please note that the mixed naming scheme used in
4  * this module is intentional.  Templates which evaluate to a type follow the
5  * naming convention used for types, and templates which evaluate to a value
6  * follow the naming convention used for functions.
7  *
8  * Copyright: Copyright (C) 2005-2006 Sean Kelly.  All rights reserved.
9  * License:   BSD style: $(LICENSE)
10  * Authors:   Sean Kelly, Fawzi Mohamed, Abscissa
11  */
12 module tango.core.Traits;
13 
14 /**
15  * Strips the qualifiers from a type
16  */
17 
18 template BaseTypeOf( T )
19 {
20     static if (is(T S : shared(S)))
21         alias S BaseTypeOf;
22     else static if (is(T S : shared(const(S))))
23         alias S BaseTypeOf;
24     else static if (is(T S : const(S)))
25         alias S BaseTypeOf;
26     else
27         alias T BaseTypeOf;
28 }
29 
30 /**
31  * Computes the effective type that inout would have if you have it two parameters of difference constness
32  */
33 
34 template InoutTypeOf(T, M)
35 {
36     static assert(is(BaseTypeOf!(T) == BaseTypeOf!(M)));
37     static if (is(immutable(BaseTypeOf!(T)) == T) && is(immutable(BaseTypeOf!(T)) == M))
38         alias immutable(BaseTypeOf!(T)) InoutTypeOf;
39     else static if ((is(BaseTypeOf!(T) == T) && is(BaseTypeOf!(T) == M)))
40         alias BaseTypeOf!(T) InoutTypeOf;
41     else
42         alias const(BaseTypeOf!(T)) InoutTypeOf;
43 }
44 
45 /**
46  * Evaluates to true if T is char[], wchar[], or dchar[].
47  */
48 template isStringType( T )
49 {
50     const bool isStringType = is( T : const(char)[] )  ||
51                               is( T : const(wchar)[] ) ||
52                               is( T : const(dchar)[] );
53 }
54 
55 /**
56  * Evaluates to true if T is char, wchar, or dchar.
57  */
58 template isCharType( T )
59 {
60     const bool isCharType = is( BaseTypeOf!(T) == char )  ||
61                             is( BaseTypeOf!(T) == wchar ) ||
62                             is( BaseTypeOf!(T) == dchar );
63 }
64 
65 
66 /**
67  * Evaluates to true if T is a signed integer type.
68  */
69 template isSignedIntegerType( T )
70 {
71     const bool isSignedIntegerType = is( BaseTypeOf!(T) == byte )  ||
72                                      is( BaseTypeOf!(T) == short ) ||
73                                      is( BaseTypeOf!(T) == int )   ||
74                                      is( BaseTypeOf!(T) == long )/+||
75                                      is( T == cent  )+/;
76 }
77 
78 
79 /**
80  * Evaluates to true if T is an unsigned integer type.
81  */
82 template isUnsignedIntegerType( T )
83 {
84     const bool isUnsignedIntegerType = is( BaseTypeOf!(T) == ubyte )  ||
85                                        is( BaseTypeOf!(T) == ushort ) ||
86                                        is( BaseTypeOf!(T) == uint )   ||
87                                        is( BaseTypeOf!(T) == ulong )/+||
88                                        is( T == ucent  )+/;
89 }
90 
91 
92 /**
93  * Evaluates to true if T is a signed or unsigned integer type.
94  */
95 template isIntegerType( T )
96 {
97     const bool isIntegerType = isSignedIntegerType!(T) ||
98                                isUnsignedIntegerType!(T);
99 }
100 
101 
102 /**
103  * Evaluates to true if T is a real floating-point type.
104  */
105 template isRealType( T )
106 {
107     const bool isRealType = is( BaseTypeOf!(T) == float )  ||
108                             is( BaseTypeOf!(T) == double ) ||
109                             is( BaseTypeOf!(T) == real );
110 }
111 
112 
113 /**
114  * Evaluates to true if T is a complex floating-point type.
115  */
116 template isComplexType( T )
117 {
118     const bool isComplexType = is( BaseTypeOf!(T) == cfloat )  ||
119                                is( BaseTypeOf!(T) == cdouble ) ||
120                                is( BaseTypeOf!(T) == creal );
121 }
122 
123 
124 /**
125  * Evaluates to true if T is an imaginary floating-point type.
126  */
127 template isImaginaryType( T )
128 {
129     const bool isImaginaryType = is( T == ifloat )  ||
130                                  is( T == idouble ) ||
131                                  is( T == ireal );
132 }
133 
134 
135 /**
136  * Evaluates to true if T is any floating-point type: real, complex, or
137  * imaginary.
138  */
139 template isFloatingPointType( T )
140 {
141     const bool isFloatingPointType = isRealType!(T)    ||
142                                      isComplexType!(T) ||
143                                      isImaginaryType!(T);
144 }
145 
146 /// true if T is an atomic type
147 template isAtomicType(T)
148 {
149     static if( is( T == bool )
150             || is( T == char )
151             || is( T == wchar )
152             || is( T == dchar )
153             || is( T == byte )
154             || is( T == short )
155             || is( T == int )
156             || is( T == long )
157             || is( T == ubyte )
158             || is( T == ushort )
159             || is( T == uint )
160             || is( T == ulong )
161             || is( T == float )
162             || is( T == double )
163             || is( T == real )
164             || is( T == ifloat )
165             || is( T == idouble )
166             || is( T == ireal ) )
167         const isAtomicType = true;
168     else
169         const isAtomicType = false;
170 }
171 
172 /**
173  * complex type for the given type
174  */
175 template ComplexTypeOf(T){
176     static if(is(T==float)||is(T==ifloat)||is(T==cfloat)){
177         alias cfloat ComplexTypeOf;
178     } else static if(is(T==double)|| is(T==idouble)|| is(T==cdouble)){
179         alias cdouble ComplexTypeOf;
180     } else static if(is(T==real)|| is(T==ireal)|| is(T==creal)){
181         alias creal ComplexTypeOf;
182     } else static assert(0,"unsupported type in ComplexTypeOf "~T.stringof);
183 }
184 
185 /**
186  * real type for the given type
187  */
188 template RealTypeOf(T){
189     static if(is(T==float)|| is(T==ifloat)|| is(T==cfloat)){
190         alias float RealTypeOf;
191     } else static if(is(T==double)|| is(T==idouble)|| is(T==cdouble)){
192         alias double RealTypeOf;
193     } else static if(is(T==real)|| is(T==ireal)|| is(T==creal)){
194         alias real RealTypeOf;
195     } else static assert(0,"unsupported type in RealTypeOf "~T.stringof);
196 }
197 
198 /**
199  * imaginary type for the given type
200  */
201 template ImaginaryTypeOf(T){
202     static if(is(T==float)|| is(T==ifloat)|| is(T==cfloat)){
203         alias ifloat ImaginaryTypeOf;
204     } else static if(is(T==double)|| is(T==idouble)|| is(T==cdouble)){
205         alias idouble ImaginaryTypeOf;
206     } else static if(is(T==real)|| is(T==ireal)|| is(T==creal)){
207         alias ireal ImaginaryTypeOf;
208     } else static assert(0,"unsupported type in ImaginaryTypeOf "~T.stringof);
209 }
210 
211 /// type with maximum precision
212 template MaxPrecTypeOf(T){
213     static if (isComplexType!(T)){
214         alias creal MaxPrecTypeOf;
215     } else static if (isImaginaryType!(T)){
216         alias ireal MaxPrecTypeOf;
217     } else {
218         alias real MaxPrecTypeOf;
219     }
220 }
221 
222 
223 /**
224  * Evaluates to true if T is a pointer type.
225  */
226 template isPointerType(T)
227 {
228         const isPointerType = false;
229 }
230 
231 template isPointerType(T : T*)
232 {
233         const isPointerType = true;
234 }
235 
236 debug( UnitTest )
237 {
238     unittest
239     {
240         static assert( is(BaseTypeOf!(const(int))==int) );
241         static assert( is(BaseTypeOf!(immutable(int))==int) );
242         static assert( is(BaseTypeOf!(shared(int))==int) );
243         static assert( is(BaseTypeOf!(inout(int))==int) );
244         static assert( isPointerType!(void*) );
245         static assert( !isPointerType!(char[]) );
246         static assert( isPointerType!(char[]*) );
247         static assert( !isPointerType!(char*[]) );
248         static assert( isPointerType!(real*) );
249         static assert( !isPointerType!(uint) );
250         static assert( is(MaxPrecTypeOf!(float)==real));
251         static assert( is(MaxPrecTypeOf!(cfloat)==creal));
252         static assert( is(MaxPrecTypeOf!(ifloat)==ireal));
253 
254         class Ham
255         {
256             void* a;
257         }
258 
259         static assert( !isPointerType!(Ham) );
260 
261         union Eggs
262         {
263             void* a;
264             uint  b;
265         }
266 
267         static assert( !isPointerType!(Eggs) );
268         static assert( isPointerType!(Eggs*) );
269 
270         struct Bacon {}
271 
272         static assert( !isPointerType!(Bacon) );
273 
274     }
275 }
276 
277 /**
278  * Evaluates to true if T is a a pointer, class, interface, or delegate.
279  */
280 template isReferenceType( T )
281 {
282 
283     const bool isReferenceType = isPointerType!(T)  ||
284                                is( T == class )     ||
285                                is( T == interface ) ||
286                                is( T == delegate );
287 }
288 
289 
290 /**
291  * Evaulates to true if T is a dynamic array type.
292  */
293 template isDynamicArrayType( T )
294 {
295     const bool isDynamicArrayType = is( typeof(T.init[0])[] == T );
296 }
297 
298 /**
299  * Evaluates to true if T is a static array type.
300  */
301 template isStaticArrayType( T : T[U], size_t U )
302 {
303     const bool isStaticArrayType = true;
304 }
305 
306 template isStaticArrayType( T )
307 {
308     const bool isStaticArrayType = false;
309 }
310 
311 /// true for array types
312 template isArrayType(T)
313 {
314     static if (is( T U : U[] ))
315         const bool isArrayType=true;
316     else
317         const bool isArrayType=false;
318 }
319 
320 debug( UnitTest )
321 {
322     unittest
323     {
324         static assert( isStaticArrayType!(char[5][2]) );
325         static assert( !isDynamicArrayType!(char[5][2]) );
326         static assert( isArrayType!(char[5][2]) );
327 
328         static assert( isStaticArrayType!(char[15]) );
329         static assert( !isStaticArrayType!(char[]) );
330 
331         static assert( isDynamicArrayType!(char[]) );
332         static assert( !isDynamicArrayType!(char[15]) );
333 
334         static assert( isArrayType!(char[15]) );
335         static assert( isArrayType!(char[]) );
336         static assert( !isArrayType!(char) );
337     }
338 }
339 
340 /**
341  * Evaluates to true if T is an associative array type.
342  */
343 template isAssocArrayType( T )
344 {
345     const bool isAssocArrayType = is( typeof(T.init.values[0])[typeof(T.init.keys[0])] == T );
346 }
347 
348 
349 /**
350  * Evaluates to true if T is a function, function pointer, delegate, or
351  * callable object.
352  */
353 template isCallableType( T )
354 {
355     const bool isCallableType = is( T == function )             ||
356                                 is( typeof(*T) == function )    ||
357                                 is( T == delegate )             ||
358                                 is( typeof(T.opCall) == function );
359 }
360 
361 
362 /**
363  * Evaluates to the return type of Fn.  Fn is required to be a callable type.
364  */
365 template ReturnTypeOf( Fn )
366 {
367     static if( is( Fn Ret == return ) )
368         alias Ret ReturnTypeOf;
369     else
370         static assert( false, "Argument has no return type." );
371 }
372 
373 /** 
374  * Returns the type that a T would evaluate to in an expression.
375  * Expr is not required to be a callable type
376  */ 
377 template ExprTypeOf( Expr )
378 {
379     static if(isCallableType!( Expr ))
380         alias ReturnTypeOf!( Expr ) ExprTypeOf;
381     else
382         alias Expr ExprTypeOf;
383 }
384 
385 
386 /**
387  * Evaluates to the return type of fn.  fn is required to be callable.
388  */
389 template ReturnTypeOf( alias fn )
390 {
391 //    static if( is( typeof(fn) Base == typedef ) )
392 //        alias ReturnTypeOf!(Base) ReturnTypeOf;
393 //    else
394         alias ReturnTypeOf!(typeof(fn)) ReturnTypeOf;
395 }
396 
397 
398 /**
399  * Evaluates to a tuple representing the parameters of Fn.  Fn is required to
400  * be a callable type.
401  */
402 template ParameterTupleOf( Fn )
403 {
404     static if( is( Fn Params == function ) )
405         alias Params ParameterTupleOf;
406     else static if( is( Fn Params == delegate ) )
407         alias ParameterTupleOf!(Params) ParameterTupleOf;
408     else static if( is( Fn Params == Params* ) )
409         alias ParameterTupleOf!(Params) ParameterTupleOf;
410     else
411         static assert( false, "Argument has no parameters." );
412 }
413 
414 
415 /**
416  * Evaluates to a tuple representing the parameters of fn.  n is required to
417  * be callable.
418  */
419 template ParameterTupleOf( alias fn )
420 {
421 //    static if( is( typeof(fn) Base == typedef ) )
422 //        alias ParameterTupleOf!(Base) ParameterTupleOf;
423 //    else
424         alias ParameterTupleOf!(typeof(fn)) ParameterTupleOf;
425 }
426 
427 
428 /**
429  * Evaluates to a tuple representing the ancestors of T.  T is required to be
430  * a class or interface type.
431  */
432 template BaseTypeTupleOf( T )
433 {
434     static if( is( T Base == super ) )
435         alias Base BaseTypeTupleOf;
436     else
437         static assert( false, "Argument is not a class or interface." );
438 }
439 
440 /**
441  * Strips the []'s off of a type.
442  */
443 template BaseTypeOfArrays(T)
444 {
445     static if( is( T S : S[]) ) {
446         alias BaseTypeOfArrays!(S)  BaseTypeOfArrays;
447     }
448     else {
449         alias T BaseTypeOfArrays;
450     }
451 }
452 
453 /**
454  * strips one [] off a type
455  */
456 template ElementTypeOfArray(T:T[])
457 {
458     alias T ElementTypeOfArray;
459 }
460 
461 /**
462  * Count the []'s on an array type
463  */
464 template rankOfArray(T) {
465     static if(is(T S : S[])) {
466         const uint rankOfArray = 1 + rankOfArray!(S);
467     } else {
468         const uint rankOfArray = 0;
469     }
470 }
471 
472 /// type of the keys of an AA
473 template KeyTypeOfAA(T){
474     alias typeof(T.init.keys[0]) KeyTypeOfAA;
475 }
476 
477 /// type of the values of an AA
478 template ValTypeOfAA(T){
479     alias typeof(T.init.values[0]) ValTypeOfAA;
480 }
481 
482 /// returns the size of a static array
483 template staticArraySize(T)
484 {
485     static assert(isStaticArrayType!(T),"staticArraySize needs a static array as type");
486     static assert(rankOfArray!(T)==1,"implemented only for 1d arrays...");
487     const size_t staticArraySize=(T).sizeof / ElementTypeOfArray!(T).sizeof;
488 }
489 
490 /// is T is static array returns a dynamic array, otherwise returns T
491 template DynamicArrayType(T)
492 {
493     static if( isStaticArrayType!(T) )
494         alias typeof(T.init.dup) DynamicArrayType;
495     else
496         alias T DynamicArrayType;
497 }
498 
499 debug( UnitTest )
500 {
501     static assert( is(BaseTypeOfArrays!(real[][])==real) );
502     static assert( is(BaseTypeOfArrays!(real[2][3])==real) );
503     static assert( is(ElementTypeOfArray!(real[])==real) );
504     static assert( is(ElementTypeOfArray!(real[][])==real[]) );
505     static assert( is(ElementTypeOfArray!(real[2][])==real[2]) );
506     static assert( is(ElementTypeOfArray!(real[2][2])==real[2]) );
507     static assert( rankOfArray!(real[][])==2 );
508     static assert( rankOfArray!(real[2][])==2 );
509     static assert( is(ValTypeOfAA!(char[int])==char));
510     static assert( is(KeyTypeOfAA!(char[int])==int));
511     static assert( is(ValTypeOfAA!(char[][int])==char[]));
512     static assert( is(KeyTypeOfAA!(char[][int[]])==const(int)[]));
513     static assert( isAssocArrayType!(char[][int[]]));
514     static assert( !isAssocArrayType!(char[]));
515     static assert( is(DynamicArrayType!(char[2])==DynamicArrayType!(char[])));
516     static assert( is(DynamicArrayType!(char[2])==char[]));
517     static assert( staticArraySize!(char[2])==2);
518 }
519 
520 // ------- CTFE -------
521 
522 /// compile time integer to string
523 char [] ctfe_i2a(int i){
524     char[] digit="0123456789".dup;
525     char[] res="".dup;
526     if (i==0){
527         return "0".dup;
528     }
529     bool neg=false;
530     if (i<0){
531         neg=true;
532         i=-i;
533     }
534     while (i>0) {
535         res=digit[i%10]~res;
536         i/=10;
537     }
538     if (neg)
539         return '-'~res;
540     else
541         return res;
542 }
543 /// ditto
544 char [] ctfe_i2a(long i){
545     char[] digit="0123456789".dup;
546     char[] res="".dup;
547     if (i==0){
548         return "0".dup;
549     }
550     bool neg=false;
551     if (i<0){
552         neg=true;
553         i=-i;
554     }
555     while (i>0) {
556         res=digit[cast(size_t)(i%10)]~res;
557         i/=10;
558     }
559     if (neg)
560         return '-'~res;
561     else
562         return res;
563 }
564 /// ditto
565 char [] ctfe_i2a(uint i){
566     const(char)[] digit="0123456789";
567     char[] res;
568     if (i==0){
569         return "0".dup;
570     }
571     bool neg=false;
572     while (i>0) {
573         res=digit[i%10]~res;
574         i/=10;
575     }
576     return res;
577 }
578 /// ditto
579 char [] ctfe_i2a(ulong i){
580     const(char)[] digit="0123456789";
581     char[] res;
582     if (i==0){
583         return "0".dup;
584     }
585     bool neg=false;
586     while (i>0) {
587         res=digit[cast(size_t)(i%10)]~res;
588         i/=10;
589     }
590     return res;
591 }
592 
593 debug( UnitTest )
594 {
595     unittest {
596     static assert( ctfe_i2a(31)=="31" );
597     static assert( ctfe_i2a(-31)=="-31" );
598     static assert( ctfe_i2a(14u)=="14" );
599     static assert( ctfe_i2a(14L)=="14" );
600     static assert( ctfe_i2a(14UL)=="14" );
601     }
602 }