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 struct Converts { double value; alias value this; }
197 
198     static assert ( isRealType!(double));
199     static assert (!isRealType!(long));
200     static assert (!isRealType!(Converts));
201 }
202 
203 
204 /*******************************************************************************
205 
206     Params:
207         T = type to check
208 
209     Returns:
210         `true` if `T` is a built-in complex floating point type
211 
212 *******************************************************************************/
213 
214 public template isComplexType( T )
215 {
216     static immutable bool isComplexType =
217            is( Unqual!(T) == cfloat )
218         || is( Unqual!(T) == cdouble )
219         || is( Unqual!(T) == creal );
220 }
221 
222 ///
223 deprecated unittest
224 {
225     static assert ( isComplexType!(cdouble));
226     static assert ( isComplexType!(const(cdouble)));
227     static assert (!isComplexType!(double));
228 }
229 
230 /*******************************************************************************
231 
232     Params:
233         T = type to check
234 
235     Returns:
236         `true` if `T` is a built-in imaginary floating point type
237 
238 *******************************************************************************/
239 
240 public template isImaginaryType( T )
241 {
242     static immutable bool isImaginaryType =
243            is( Unqual!(T) == ifloat )
244         || is( Unqual!(T) == idouble )
245         || is( Unqual!(T) == ireal );
246 }
247 
248 ///
249 deprecated unittest
250 {
251     static assert ( isImaginaryType!(idouble));
252     static assert ( isImaginaryType!(const(idouble)));
253     static assert (!isImaginaryType!(double));
254 }
255 
256 /*******************************************************************************
257 
258     Params:
259         T = type to check
260 
261     Returns:
262         `true` if `T` is any built-in floating-point type: real, complex, or
263         imaginary.
264 
265 *******************************************************************************/
266 
267 public template isFloatingPointType( T )
268 {
269     static immutable bool isFloatingPointType =
270            isRealType!(T)
271         || isComplexType!(T)
272         || isImaginaryType!(T);
273 }
274 
275 ///
276 unittest
277 {
278     static assert (isFloatingPointType!(double));
279     static assert (isFloatingPointType!(ifloat));
280 }
281 
282 /*******************************************************************************
283 
284     Used by other traits to distinguish between dynamic and static arrays
285     instead of plain `bool` values.
286 
287     `ArrayKind.NotArray` is explicitly defined to have value `0` so that it can
288     be in regular condition, i.e. `static if (isArrayType!(T))`.
289 
290 *******************************************************************************/
291 
292 public enum ArrayKind
293 {
294     NotArray    = 0,
295     Static      = 1,
296     Dynamic     = 2,
297     Associative = 3,
298 }
299 
300 /*******************************************************************************
301 
302     Check if type is an array type and which kind of array it is
303 
304     Params:
305         T = type to check
306 
307     Returns:
308         `ArrayKind` value indicating if `T` is an array and if it is, static or
309         dynamic
310 
311 *******************************************************************************/
312 
313 public template isArrayType ( T )
314 {
315     static if (is(Unqual!(T) U == U[]))
316         static immutable isArrayType = ArrayKind.Dynamic;
317     else static if (is(Unqual!(T) U : U[]))
318         static immutable isArrayType = ArrayKind.Static;
319     else static if (is(typeof(T.init.values[0])[typeof(T.init.keys[0])] ==
320             Unqual!(T)))
321         static immutable isArrayType = ArrayKind.Associative;
322     else
323         static immutable isArrayType = ArrayKind.NotArray;
324 }
325 
326 ///
327 unittest
328 {
329     static assert ( isArrayType!(char[15]) == ArrayKind.Static);
330     static assert ( isArrayType!(char[]) == ArrayKind.Dynamic);
331     static assert ( isArrayType!(char[][5]) == ArrayKind.Static);
332     static assert ( isArrayType!(char) == ArrayKind.NotArray);
333     static assert ( isArrayType!(int[int]) == ArrayKind.Associative);
334     static assert ( isArrayType!(const(int[int])) == ArrayKind.Associative);
335     static assert (!isArrayType!(char));
336 }
337 
338 unittest
339 {
340     static struct S { }
341     static assert (!isArrayType!(S));
342     static assert ( isArrayType!(S[5]));
343 }
344 
345 /*******************************************************************************
346 
347     Params:
348         T = any type
349 
350     Returns:
351         `true` if T is static or dynamic array, `false` otherwise
352 
353 *******************************************************************************/
354 
355 public template isBasicArrayType ( T )
356 {
357     static immutable isBasicArrayType =
358            (isArrayType!(T) == ArrayKind.Static)
359         || (isArrayType!(T) == ArrayKind.Dynamic);
360 }
361 
362 ///
363 unittest
364 {
365     static assert ( isBasicArrayType!(int[]));
366     static assert ( isBasicArrayType!(int[5]));
367     static assert (!isBasicArrayType!(int[int]));
368 }
369 
370 /*******************************************************************************
371 
372     Params:
373         T = static array type
374 
375     Returns:
376         for static array T[N] returns N
377 
378 *******************************************************************************/
379 
380 public template staticArrayLength ( T : U[Dim], U, size_t Dim )
381 {
382     static immutable staticArrayLength = Dim;
383 }
384 
385 unittest
386 {
387     static assert (staticArrayLength!(int[][5]) == 5);
388     static assert (staticArrayLength!(char[42]) == 42);
389     static assert (staticArrayLength!(immutable(mstring[2])) == 2);
390 }
391 
392 /*******************************************************************************
393 
394     Params:
395         T = type to check
396 
397     Returns:
398         `true` if `T` is a pointer type.
399 
400 *******************************************************************************/
401 
402 template isPointerType ( T )
403 {
404     static if (is(Unqual!(T) U == U*))
405         static immutable isPointerType = true;
406     else
407         static immutable isPointerType = false;
408 }
409 
410 ///
411 unittest
412 {
413     static assert ( isPointerType!(void*));
414     static assert (!isPointerType!(char[]));
415 }
416 
417 unittest
418 {
419     static assert ( isPointerType!(char[]*));
420     static assert (!isPointerType!(char*[]));
421     static assert ( isPointerType!(const(real)*));
422     static assert (!isPointerType!(uint));
423 
424     class Ham { void* a; }
425 
426     static assert (!isPointerType!(Ham));
427 
428     union Eggs
429     {
430         void* a;
431         uint  b;
432     }
433 
434     static assert (!isPointerType!(Eggs));
435     static assert ( isPointerType!(immutable(Eggs*)));
436 
437     struct Bacon { }
438 
439     static assert (!isPointerType!(Bacon));
440 
441     // function pointer is a pointer, but delegate is not:
442     void foo () {}
443     static void bar () {}
444     static assert (!isPointerType!(typeof(&foo)));
445     static assert ( isPointerType!(typeof(&bar)));
446 }
447 
448 /*******************************************************************************
449 
450     Params:
451         T = type to check
452 
453     Returns:
454         `true` if `T` is a struct, class, interface or union
455 
456 *******************************************************************************/
457 
458 template isReferenceType ( T )
459 {
460     static immutable isReferenceType =
461            isPointerType!(T)
462         || is(T == delegate)
463         || isArrayType!(T) == ArrayKind.Dynamic
464         || isArrayType!(T) == ArrayKind.Associative
465         || is(T == class)
466         || is(T == interface);
467 }
468 
469 ///
470 unittest
471 {
472     struct S { }
473     class C { }
474     interface I { }
475 
476     static assert (!isReferenceType!(S));
477     static assert ( isReferenceType!(S*));
478     static assert ( isReferenceType!(S[]));
479     static assert ( isReferenceType!(C));
480     static assert ( isReferenceType!(I));
481     static assert ( isReferenceType!(S[C]));
482     static assert ( isReferenceType!(void function(int)));
483 
484     static void foo ( ) { }
485     static assert (!isReferenceType!(typeof(foo)));
486 }
487 
488 /*******************************************************************************
489 
490     Params:
491         T = type to check
492 
493     Returns:
494         `true` if `T` is a struct, class, interface or union
495 
496 *******************************************************************************/
497 
498 template isAggregateType ( T )
499 {
500     static immutable isAggregateType =
501            is(T == class)
502         || is(T == interface)
503         || is(T == struct)
504         || is(T == union);
505 }
506 
507 ///
508 unittest
509 {
510     struct S { int x; }
511     union U { int x; double y; }
512 
513     static assert ( isAggregateType!(S));
514     static assert ( isAggregateType!(U));
515     static assert (!isAggregateType!(S[2]));
516 }
517 
518 /*******************************************************************************
519 
520     Params:
521         T = type to check
522 
523     Returns:
524         `true` if T is a function, function pointer or delegate
525 
526 *******************************************************************************/
527 
528 template isFunctionType ( T )
529 {
530     static immutable bool isFunctionType =
531            is(T == function)
532         || is(typeof(*T.init) == function)
533         || is(T == delegate);
534 }
535 
536 ///
537 unittest
538 {
539     void foo1() { }
540     auto foo2 = () { };
541     static void foo3() { }
542 
543     static assert (isFunctionType!(typeof(&foo1)));
544     static assert (isFunctionType!(typeof(foo2)));
545     static assert (isFunctionType!(typeof(&foo3)));
546     static assert (isFunctionType!(typeof(foo3)));
547 }
548 
549 unittest
550 {
551     static real func(ref int) { return 0; }
552     void nestedFunc() { }
553     class C
554     {
555         real method(ref int) { return 0; }
556     }
557     auto c = new C;
558     auto fp = &func;
559     auto dg = &c.method;
560     real val;
561 
562     static assert ( isFunctionType!(typeof(func)));
563     static assert ( isFunctionType!(typeof(nestedFunc)));
564     static assert ( isFunctionType!(typeof(C.method)));
565     static assert ( isFunctionType!(typeof(fp)));
566     static assert ( isFunctionType!(typeof(dg)));
567     static assert ( isFunctionType!(real function(ref int)));
568     static assert ( isFunctionType!(real delegate(ref int)));
569     static assert ( isFunctionType!(typeof((int a) { return a; })));
570 
571     static assert (!isFunctionType!(int));
572     static assert (!isFunctionType!(typeof(val)));
573 }
574 
575 /*******************************************************************************
576 
577     Params:
578         T = type to check
579 
580     Returns:
581         `true` if T is a function, function pointer or delegate or callable
582         aggregate
583 
584 *******************************************************************************/
585 
586 template isCallableType ( T )
587 {
588     static if (is(typeof(T.opCall)))
589         static immutable isCallableType = isFunctionType!(typeof(&T.init.opCall));
590     else
591         static immutable isCallableType = isFunctionType!(T);
592 }
593 
594 ///
595 unittest
596 {
597     struct S
598     {
599         void opCall (int) { }
600     }
601 
602     static assert (isCallableType!(S));
603     static assert (isCallableType!(typeof(S.opCall)));
604 }
605 
606 unittest
607 {
608     struct S2
609     {
610         static bool opCall( in int p1, in int p2 ) { return true; }
611     }
612 
613     static assert (isCallableType!(S2));
614 }
615 
616 /*******************************************************************************
617 
618     Used as result type for `isTypedef` trait.
619 
620     `None` value is explicitly set to `0` so that it can be used in condition
621     like `if(isTypedef!(T))`.
622 
623 *******************************************************************************/
624 
625 public enum TypedefKind
626 {
627     /// Not a typedef
628     None = 0,
629     /// D1 `typedef` keyword
630     Keyword,
631     /// Emulated by struct
632     Struct
633 }
634 
635 /*******************************************************************************
636 
637     Determines if T is a typedef of some kind
638 
639     Params:
640         T = type to check
641 
642     Evaluates to:
643         `TypedefKind` value which is non-zero is T is some typedef
644 
645 *******************************************************************************/
646 
647 public template isTypedef (T)
648 {
649     static if (is(T.IsTypedef))
650         static immutable isTypedef = TypedefKind.Struct;
651     else
652         static immutable isTypedef = TypedefKind.None;
653 }
654 
655 unittest
656 {
657     mixin(Typedef!(double, "RealNum"));
658 
659     static assert(!isTypedef!(int));
660     static assert(!isTypedef!(double));
661     static assert( isTypedef!(RealNum));
662 
663     static assert(isTypedef!(RealNum) == TypedefKind.Struct);
664 }
665 
666 /******************************************************************************
667 
668     Check if the type of a member inside a struct or class is a D manifest
669     constant
670 
671     Params:
672         T = The struct that will be checked
673         name = The struct member that will be checked
674 
675 ******************************************************************************/
676 
677 bool isManifestConstant ( T, string name ) ( )
678 {
679     T instance;
680     mixin(`return is(typeof(instance.`~name~`)) && !is(typeof(&instance.`~name~`));`);
681 }
682 
683 unittest
684 {
685     struct Test
686     {
687         enum manifest = 2;
688         int notManifest1;
689         enum CustomType : int {
690             a = 1, b = 2
691         }
692 
693         CustomType notManifest2;
694     }
695 
696     static assert(isManifestConstant!(Test, "manifest"));
697     static assert(!isManifestConstant!(Test, "CustomType"));
698     static assert(!isManifestConstant!(Test, "notManifest1"));
699     static assert(!isManifestConstant!(Test, "notManifest2"));
700 }
701 
702 
703 
704 /******************************************************************************
705 
706     Check if a constant is manifest constant
707 
708     Params:
709         T = The constant
710 
711 ******************************************************************************/
712 
713 bool isManifestConstant ( alias T ) ( )
714 {
715     return is(typeof(T)) && !is(typeof(&T));
716 }
717 
718 unittest
719 {
720     enum manifest = 3;
721     int notManifest;
722 
723     static assert(isManifestConstant!(manifest));
724     static assert(!isManifestConstant!(notManifest));
725 }