1 /*******************************************************************************
2 
3     Basic traits allowing to distinguish various types between each other. Any
4     more convoluted traits dedicated to specific type kinds should go in
5     dedicated modules.
6 
7     NB: because this module is often used as purely compile-time dependency it
8         used built-in asserts instead of `ocean.core.Test` to reduce amount of
9         cyclic imports. `ocean.meta` modules in general are not supposed to
10         import anything outside of `ocean.meta`.
11 
12     Copyright:
13         Copyright (C) 2017 dunnhumby Germany GmbH. All rights reserved.
14 
15     License:
16         Boost Software License Version 1.0. See LICENSE_BOOST.txt for details.
17         Alternatively, this file may be distributed under the terms of the Tango
18         3-Clause BSD License (see LICENSE_BSD.txt for details).
19 
20 *******************************************************************************/
21 
22 module ocean.meta.traits.Basic;
23 
24 import ocean.meta.types.Qualifiers;
25 import ocean.meta.types.Typedef;
26 
27 /*******************************************************************************
28 
29     Primitive types are the types from which one or multiple other types cannot
30     be derived from using the ``is()`` expression or corresponding template type
31     parameter specialisation. The following types are not primitive:
32      - arrays (static, dynamic and associative) and pointers,
33      - classes structs and unions,
34      - delegates, function pointers and functions (function pointer base types),
35      - enums and typedefs.
36 
37     All other, including arithmetic and character types are primitive. Each
38     primitive type is represented by a D keyword.
39     ``void`` is a primitive type. Imaginary and complex numbers are considered
40     primitive types, too, which may be subject to discussion.
41 
42     Params:
43         T = type to check
44 
45     Returns:
46         `true` if `T` is a primitive type
47 
48 *******************************************************************************/
49 
50 public template isPrimitiveType ( T )
51 {
52     static immutable isPrimitiveType =
53            is(Unqual!(T) == void)
54         || is(Unqual!(T) == bool)
55         || isIntegerType!(T)
56         || isCharType!(T)
57         || isFloatingPointType!(T);
58 }
59 
60 ///
61 unittest
62 {
63     static assert ( isPrimitiveType!(int));
64     static assert (!isPrimitiveType!(int*));
65     struct S { }
66     static assert (!isPrimitiveType!(S));
67 }
68 
69 /*******************************************************************************
70 
71     Params:
72         T = type to check
73 
74     Returns:
75         `true` if `T` is one of supported string element types
76 
77 *******************************************************************************/
78 
79 public template isCharType ( T )
80 {
81     static immutable bool isCharType =
82            is(Unqual!(T) == char)
83         || is(Unqual!(T) == wchar)
84         || is(Unqual!(T) == dchar);
85 }
86 
87 ///
88 unittest
89 {
90     static assert ( isCharType!(wchar));
91     static assert ( isCharType!(const(char)));
92     static assert (!isCharType!(byte));
93 }
94 
95 /*******************************************************************************
96 
97     Params:
98         T = type to check
99 
100     Returns:
101         `true` if `T` is a built-in signed integer type
102 
103 *******************************************************************************/
104 
105 public template isSignedIntegerType ( T )
106 {
107     static immutable bool isSignedIntegerType =
108            is(Unqual!(T) == byte)
109         || is(Unqual!(T) == short)
110         || is(Unqual!(T) == int)
111         || is(Unqual!(T) == long);
112 }
113 
114 ///
115 unittest
116 {
117     static assert ( isSignedIntegerType!(int));
118     static assert ( isSignedIntegerType!(const(long)));
119     static assert (!isSignedIntegerType!(ubyte));
120 }
121 
122 /*******************************************************************************
123 
124     Params:
125         T = type to check
126 
127     Returns:
128         `true` if `T` is a built-in unsigned integer type
129 
130 *******************************************************************************/
131 
132 public template isUnsignedIntegerType ( T )
133 {
134     static immutable bool isUnsignedIntegerType =
135            is(Unqual!(T) == ubyte)
136         || is(Unqual!(T) == ushort)
137         || is(Unqual!(T) == uint)
138         || is(Unqual!(T) == ulong);
139 }
140 
141 ///
142 unittest
143 {
144     static assert (!isUnsignedIntegerType!(int));
145     static assert ( isUnsignedIntegerType!(ubyte));
146     static assert ( isUnsignedIntegerType!(const(ulong)));
147 }
148 
149 /*******************************************************************************
150 
151     Params:
152         T = type to check
153 
154     Returns:
155         `true` if `T` is a built-in integer type
156 
157 *******************************************************************************/
158 
159 public template isIntegerType ( T )
160 {
161     static immutable bool isIntegerType =
162            isSignedIntegerType!(T)
163         || isUnsignedIntegerType!(T);
164 }
165 
166 ///
167 unittest
168 {
169     static assert ( isIntegerType!(long));
170     static assert ( isIntegerType!(ubyte));
171     static assert (!isIntegerType!(char));
172 }
173 
174 /*******************************************************************************
175 
176     Params:
177         T = type to check
178 
179     Returns:
180         `true` if `T` is a built-in floating point type, excluding
181         complex/imaginary ones
182 
183 *******************************************************************************/
184 
185 public template isRealType ( T )
186 {
187     static immutable bool isRealType =
188            is( Unqual!(T) == float )
189         || is( Unqual!(T) == double )
190         || is( Unqual!(T) == real );
191 }
192 
193 ///
194 unittest
195 {
196     static assert ( isRealType!(double));
197     static assert (!isRealType!(long));
198     static assert (!isRealType!(cdouble));
199 }
200 
201 
202 /*******************************************************************************
203 
204     Params:
205         T = type to check
206 
207     Returns:
208         `true` if `T` is a built-in complex floating point type
209 
210 *******************************************************************************/
211 
212 public template isComplexType( T )
213 {
214     static immutable bool isComplexType =
215            is( Unqual!(T) == cfloat )
216         || is( Unqual!(T) == cdouble )
217         || is( Unqual!(T) == creal );
218 }
219 
220 ///
221 unittest
222 {
223     static assert ( isComplexType!(cdouble));
224     static assert ( isComplexType!(const(cdouble)));
225     static assert (!isComplexType!(double));
226 }
227 
228 /*******************************************************************************
229 
230     Params:
231         T = type to check
232 
233     Returns:
234         `true` if `T` is a built-in imaginary floating point type
235 
236 *******************************************************************************/
237 
238 public template isImaginaryType( T )
239 {
240     static immutable bool isImaginaryType =
241            is( Unqual!(T) == ifloat )
242         || is( Unqual!(T) == idouble )
243         || is( Unqual!(T) == ireal );
244 }
245 
246 ///
247 unittest
248 {
249     static assert ( isImaginaryType!(idouble));
250     static assert ( isImaginaryType!(const(idouble)));
251     static assert (!isImaginaryType!(double));
252 }
253 
254 /*******************************************************************************
255 
256     Params:
257         T = type to check
258 
259     Returns:
260         `true` if `T` is any built-in floating-point type: real, complex, or
261         imaginary.
262 
263 *******************************************************************************/
264 
265 public template isFloatingPointType( T )
266 {
267     static immutable bool isFloatingPointType =
268            isRealType!(T)
269         || isComplexType!(T)
270         || isImaginaryType!(T);
271 }
272 
273 ///
274 unittest
275 {
276     static assert (isFloatingPointType!(double));
277     static assert (isFloatingPointType!(ifloat));
278 }
279 
280 /*******************************************************************************
281 
282     Used by other traits to distinguish between dynamic and static arrays
283     instead of plain `bool` values.
284 
285     `ArrayKind.NotArray` is explicitly defined to have value `0` so that it can
286     be in regular condition, i.e. `static if (isArrayType!(T))`.
287 
288 *******************************************************************************/
289 
290 public enum ArrayKind
291 {
292     NotArray    = 0,
293     Static      = 1,
294     Dynamic     = 2,
295     Associative = 3,
296 }
297 
298 /*******************************************************************************
299 
300     Check if type is an array type and which kind of array it is
301 
302     Params:
303         T = type to check
304 
305     Returns:
306         `ArrayKind` value indicating if `T` is an array and if it is, static or
307         dynamic
308 
309 *******************************************************************************/
310 
311 public template isArrayType ( T )
312 {
313     static if (is(Unqual!(T) U == U[]))
314         static immutable isArrayType = ArrayKind.Dynamic;
315     else static if (is(Unqual!(T) U : U[]))
316         static immutable isArrayType = ArrayKind.Static;
317     else static if (is(typeof(T.init.values[0])[typeof(T.init.keys[0])] ==
318             Unqual!(T)))
319         static immutable isArrayType = ArrayKind.Associative;
320     else
321         static immutable isArrayType = ArrayKind.NotArray;
322 }
323 
324 ///
325 unittest
326 {
327     static assert ( isArrayType!(char[15]) == ArrayKind.Static);
328     static assert ( isArrayType!(char[]) == ArrayKind.Dynamic);
329     static assert ( isArrayType!(char[][5]) == ArrayKind.Static);
330     static assert ( isArrayType!(char) == ArrayKind.NotArray);
331     static assert ( isArrayType!(int[int]) == ArrayKind.Associative);
332     static assert ( isArrayType!(const(int[int])) == ArrayKind.Associative);
333     static assert (!isArrayType!(char));
334 }
335 
336 unittest
337 {
338     static struct S { }
339     static assert (!isArrayType!(S));
340     static assert ( isArrayType!(S[5]));
341 }
342 
343 /*******************************************************************************
344 
345     Params:
346         T = any type
347 
348     Returns:
349         `true` if T is static or dynamic array, `false` otherwise
350 
351 *******************************************************************************/
352 
353 public template isBasicArrayType ( T )
354 {
355     static immutable isBasicArrayType =
356            (isArrayType!(T) == ArrayKind.Static)
357         || (isArrayType!(T) == ArrayKind.Dynamic);
358 }
359 
360 ///
361 unittest
362 {
363     static assert ( isBasicArrayType!(int[]));
364     static assert ( isBasicArrayType!(int[5]));
365     static assert (!isBasicArrayType!(int[int]));
366 }
367 
368 /*******************************************************************************
369 
370     Params:
371         T = static array type
372 
373     Returns:
374         for static array T[N] returns N
375 
376 *******************************************************************************/
377 
378 public template staticArrayLength ( T : U[Dim], U, size_t Dim )
379 {
380     static immutable staticArrayLength = Dim;
381 }
382 
383 unittest
384 {
385     static assert (staticArrayLength!(int[][5]) == 5);
386     static assert (staticArrayLength!(char[42]) == 42);
387     static assert (staticArrayLength!(immutable(mstring[2])) == 2);
388 }
389 
390 /*******************************************************************************
391 
392     Params:
393         T = type to check
394 
395     Returns:
396         `true` if `T` is a pointer type.
397 
398 *******************************************************************************/
399 
400 template isPointerType ( T )
401 {
402     static if (is(Unqual!(T) U == U*))
403         static immutable isPointerType = true;
404     else
405         static immutable isPointerType = false;
406 }
407 
408 ///
409 unittest
410 {
411     static assert ( isPointerType!(void*));
412     static assert (!isPointerType!(char[]));
413 }
414 
415 unittest
416 {
417     static assert ( isPointerType!(char[]*));
418     static assert (!isPointerType!(char*[]));
419     static assert ( isPointerType!(const(real)*));
420     static assert (!isPointerType!(uint));
421 
422     class Ham { void* a; }
423 
424     static assert (!isPointerType!(Ham));
425 
426     union Eggs
427     {
428         void* a;
429         uint  b;
430     }
431 
432     static assert (!isPointerType!(Eggs));
433     static assert ( isPointerType!(immutable(Eggs*)));
434 
435     struct Bacon { }
436 
437     static assert (!isPointerType!(Bacon));
438 
439     // function pointer is a pointer, but delegate is not:
440     void foo () {}
441     static void bar () {}
442     static assert (!isPointerType!(typeof(&foo)));
443     static assert ( isPointerType!(typeof(&bar)));
444 }
445 
446 /*******************************************************************************
447 
448     Params:
449         T = type to check
450 
451     Returns:
452         `true` if `T` is a struct, class, interface or union
453 
454 *******************************************************************************/
455 
456 template isReferenceType ( T )
457 {
458     static immutable isReferenceType =
459            isPointerType!(T)
460         || is(T == delegate)
461         || isArrayType!(T) == ArrayKind.Dynamic
462         || isArrayType!(T) == ArrayKind.Associative
463         || is(T == class)
464         || is(T == interface);
465 }
466 
467 ///
468 unittest
469 {
470     struct S { }
471     class C { }
472     interface I { }
473 
474     static assert (!isReferenceType!(S));
475     static assert ( isReferenceType!(S*));
476     static assert ( isReferenceType!(S[]));
477     static assert ( isReferenceType!(C));
478     static assert ( isReferenceType!(I));
479     static assert ( isReferenceType!(S[C]));
480     static assert ( isReferenceType!(void function(int)));
481 
482     static void foo ( ) { }
483     static assert (!isReferenceType!(typeof(foo)));
484 }
485 
486 /*******************************************************************************
487 
488     Params:
489         T = type to check
490 
491     Returns:
492         `true` if `T` is a struct, class, interface or union
493 
494 *******************************************************************************/
495 
496 template isAggregateType ( T )
497 {
498     static immutable isAggregateType =
499            is(T == class)
500         || is(T == interface)
501         || is(T == struct)
502         || is(T == union);
503 }
504 
505 ///
506 unittest
507 {
508     struct S { int x; }
509     union U { int x; double y; }
510 
511     static assert ( isAggregateType!(S));
512     static assert ( isAggregateType!(U));
513     static assert (!isAggregateType!(S[2]));
514 }
515 
516 /*******************************************************************************
517 
518     Params:
519         T = type to check
520 
521     Returns:
522         `true` if T is a function, function pointer or delegate
523 
524 *******************************************************************************/
525 
526 template isFunctionType ( T )
527 {
528     static immutable bool isFunctionType =
529            is(T == function)
530         || is(typeof(*T.init) == function)
531         || is(T == delegate);
532 }
533 
534 ///
535 unittest
536 {
537     void foo1() { }
538     auto foo2 = () { };
539     static void foo3() { }
540 
541     static assert (isFunctionType!(typeof(&foo1)));
542     static assert (isFunctionType!(typeof(foo2)));
543     static assert (isFunctionType!(typeof(&foo3)));
544     static assert (isFunctionType!(typeof(foo3)));
545 }
546 
547 unittest
548 {
549     static real func(ref int) { return 0; }
550     void nestedFunc() { }
551     class C
552     {
553         real method(ref int) { return 0; }
554     }
555     auto c = new C;
556     auto fp = &func;
557     auto dg = &c.method;
558     real val;
559 
560     static assert ( isFunctionType!(typeof(func)));
561     static assert ( isFunctionType!(typeof(nestedFunc)));
562     static assert ( isFunctionType!(typeof(C.method)));
563     static assert ( isFunctionType!(typeof(fp)));
564     static assert ( isFunctionType!(typeof(dg)));
565     static assert ( isFunctionType!(real function(ref int)));
566     static assert ( isFunctionType!(real delegate(ref int)));
567     static assert ( isFunctionType!(typeof((int a) { return a; })));
568 
569     static assert (!isFunctionType!(int));
570     static assert (!isFunctionType!(typeof(val)));
571 }
572 
573 /*******************************************************************************
574 
575     Params:
576         T = type to check
577 
578     Returns:
579         `true` if T is a function, function pointer or delegate or callable
580         aggregate
581 
582 *******************************************************************************/
583 
584 template isCallableType ( T )
585 {
586     static if (is(typeof(T.opCall)))
587         static immutable isCallableType = isFunctionType!(typeof(&T.init.opCall));
588     else
589         static immutable isCallableType = isFunctionType!(T);
590 }
591 
592 ///
593 unittest
594 {
595     struct S
596     {
597         void opCall (int) { }
598     }
599 
600     static assert (isCallableType!(S));
601     static assert (isCallableType!(typeof(S.opCall)));
602 }
603 
604 unittest
605 {
606     struct S2
607     {
608         static bool opCall( in int p1, in int p2 ) { return true; }
609     }
610 
611     static assert (isCallableType!(S2));
612 }
613 
614 /*******************************************************************************
615 
616     Used as result type for `isTypedef` trait.
617 
618     `None` value is explicitly set to `0` so that it can be used in condition
619     like `if(isTypedef!(T))`.
620 
621 *******************************************************************************/
622 
623 public enum TypedefKind
624 {
625     /// Not a typedef
626     None = 0,
627     /// D1 `typedef` keyword
628     Keyword,
629     /// Emulated by struct
630     Struct
631 }
632 
633 /*******************************************************************************
634 
635     Determines if T is a typedef of some kind
636 
637     Params:
638         T = type to check
639 
640     Evaluates to:
641         `TypedefKind` value which is non-zero is T is some typedef
642 
643 *******************************************************************************/
644 
645 public template isTypedef (T)
646 {
647     static if (is(T.IsTypedef))
648         static immutable isTypedef = TypedefKind.Struct;
649     else
650         static immutable isTypedef = TypedefKind.None;
651 }
652 
653 unittest
654 {
655     mixin(Typedef!(double, "RealNum"));
656 
657     static assert(!isTypedef!(int));
658     static assert(!isTypedef!(double));
659     static assert( isTypedef!(RealNum));
660 
661     static assert(isTypedef!(RealNum) == TypedefKind.Struct);
662 }
663 
664 /******************************************************************************
665 
666     Check if the type of a member inside a struct or class is a D manifest
667     constant
668 
669     Params:
670         T = The struct that will be checked
671         name = The struct member that will be checked
672 
673 ******************************************************************************/
674 
675 bool isManifestConstant ( T, string name ) ( )
676 {
677     T instance;
678     mixin(`return is(typeof(instance.`~name~`)) && !is(typeof(&instance.`~name~`));`);
679 }
680 
681 unittest
682 {
683     struct Test
684     {
685         enum manifest = 2;
686         int notManifest1;
687         enum CustomType : int {
688             a = 1, b = 2
689         }
690 
691         CustomType notManifest2;
692     }
693 
694     static assert(isManifestConstant!(Test, "manifest"));
695     static assert(!isManifestConstant!(Test, "CustomType"));
696     static assert(!isManifestConstant!(Test, "notManifest1"));
697     static assert(!isManifestConstant!(Test, "notManifest2"));
698 }
699 
700 
701 
702 /******************************************************************************
703 
704     Check if a constant is manifest constant
705 
706     Params:
707         T = The constant
708 
709 ******************************************************************************/
710 
711 bool isManifestConstant ( alias T ) ( )
712 {
713     return is(typeof(T)) && !is(typeof(&T));
714 }
715 
716 unittest
717 {
718     enum manifest = 3;
719     int notManifest;
720 
721     static assert(isManifestConstant!(manifest));
722     static assert(!isManifestConstant!(notManifest));
723 }