1 /*******************************************************************************
2 
3     Useful functions & templates.
4 
5     More of the kind of thing you'd find in ocean.core.Traits...
6 
7     Copyright:
8         Copyright (C) 2005-2006 Sean Kelly.
9         Some parts copyright (c) 2009-2016 dunnhumby Germany GmbH.
10         All rights reserved.
11 
12     License:
13        Tango Dual License: 3-Clause BSD License / Academic Free License v3.0.
14        See LICENSE_TANGO.txt for details.
15 
16     Authors: Sean Kelly, Fawzi Mohamed, Abscissa
17 
18 *******************************************************************************/
19 
20 module ocean.core.Traits;
21 
22 
23 import ocean.transition;
24 
25 import ocean.core.Tuple: Tuple;
26 
27 /*******************************************************************************
28 
29     If T is enum, aliases to its base type. Otherwise aliases to T.
30 
31     Params:
32         T = any type
33 
34 *******************************************************************************/
35 
36 deprecated("Use ocean.meta.types.Enum.EnumBaseType")
37 public template StripEnum(T)
38 {
39     static if (is(T U == enum))
40     {
41         alias U StripEnum;
42     }
43     else
44     {
45         alias T StripEnum;
46     }
47 }
48 
49 /*******************************************************************************
50 
51     Evaluates to true if T is a primitive type or false otherwise. Primitive
52     types are the types from which one or multiple other types cannot be
53     derived from using the ``is()`` expression or corresponding template
54     type parameter specialisation. The following types are not primitive:
55      - arrays (static, dynamic and associative) and pointers,
56      - classes structs and unions,
57      - delegates, function pointers and functions (function pointer base types),
58      - enums and typedefs.
59 
60     All other, including arithmetic and character types are primitive. Each
61     primitive type is represented by a D keyword.
62     ``void`` is a primitive type. Imaginary and complex numbers are considered
63     primitive types, too, which may be subject to discussion.
64 
65     Params:
66         T = type to check
67 
68 *******************************************************************************/
69 
70 deprecated("Use ocean.meta.traits.Basic.isPrimitiveType")
71 public template isPrimitiveType ( T )
72 {
73     static immutable isPrimitiveType =
74         is(Unqual!(T) == void)
75      || is(Unqual!(T) == bool)
76      || isIntegerType!(T)
77      || isCharType!(T)
78      || isFloatingPointType!(T);
79 }
80 
81 /*******************************************************************************
82 
83     Evaluates to true if a variable of any type in T is a reference type or has
84     members or elements of reference types. References are
85      - dynamic and associative arrays,
86      - pointers (including function pointers) and delegates,
87      - classes.
88 
89     Types that are not suitable to declare a variable, i.e. ``void`` and
90     function types (the base types of function pointers) are not references.
91 
92     If T is empty then the result is false.
93 
94     Params:
95         T = types to check (with no type the result is false)
96 
97 *******************************************************************************/
98 
99 deprecated("Use ocean.meta.traits.Indirections.hasIndirections")
100 public template hasIndirections ( T... )
101 {
102     static immutable hasIndirections = hasIndirectionsImpl!(T)();
103 }
104 
105 private bool hasIndirectionsImpl ( T... )()
106 {
107     static if ( T.length == 0 )
108     {
109         return false;
110     }
111     else
112     {
113         alias StripEnum!(StripTypedef!(Unqual!(T[0]))) Type;
114 
115         static if ( isPrimitiveType!(Type) || is(Type == function) )
116         {
117             return hasIndirections!(T[1..$]);
118         }
119         else static if ( isStaticArrayType!(Type) )
120         {
121             return hasIndirections!(ElementTypeOfArray!(Type)) ||
122                    hasIndirections!(T[1..$]);
123         }
124         else static if ( is ( T[0] == struct ) || is ( T[0] == union ) )
125         {
126             static if ( TypeTuple!(Type).length == 0 )
127             {
128                 return hasIndirections!(T[1..$]);
129             }
130             else static if ( TypeTuple!(Type).length == 1 )
131             {
132                 return hasIndirections!(TypeTuple!(Type)) ||
133                        hasIndirections!(T[1..$]);
134             }
135             else
136             {
137                 return hasIndirections!(TypeTuple!(Type)[0]) ||
138                        hasIndirections!(TypeTuple!(Type)[1..$]) ||
139                        hasIndirections!(T[1..$]);
140             }
141         }
142         else
143         {
144             return true;
145         }
146     }
147 
148     assert(false);
149 }
150 
151 deprecated("Use ocean.meta.traits.containsMultiDimensionalDynamicArrays")
152 public template hasMultiDimensionalDynamicArrays ( T )
153 {
154     /*
155      * typeof(hasMultiDimensionalDynamicArraysImpl!(T)()) is bool. Its purpose
156      * is to instantiate the hasMultiDimensionalDynamicArraysImpl!(T) function
157      * template before calling the function (at compile time) to work around a
158      * DMD1 bug if T contains itself like "struct T {T[] t;}".
159      */
160 
161     static immutable typeof(hasMultiDimensionalDynamicArraysImpl!(T)()) hasMultiDimensionalDynamicArrays = hasMultiDimensionalDynamicArraysImpl!(T)();
162 }
163 
164 private bool hasMultiDimensionalDynamicArraysImpl ( T ) ()
165 {
166     alias StripEnum!(StripTypedef!(T)) Type;
167 
168     static if (is(Type Element: Element[])) // dynamic or static array of Element
169     {
170         static if (is(Type == Element[])) // dynamic array of Element
171         {
172             static if (isDynamicArrayType!(Element))
173             {
174                 return true;
175             }
176             else
177             {
178                 return hasMultiDimensionalDynamicArraysImpl!(Element);
179             }
180         }
181         else  // static array of Element
182         {
183             return hasMultiDimensionalDynamicArraysImpl!(Element);
184         }
185     }
186     else static if (is(Type == struct) || is(Type == union))
187     {
188         bool result = false;
189 
190         foreach (Field; typeof(Type.tupleof))
191         {
192             static if (hasMultiDimensionalDynamicArraysImpl!(Field)())
193             {
194                 result = true;
195             }
196         }
197 
198         return result;
199     }
200     else
201     {
202         static assert(isPrimitiveType!(Type),
203                       "T expected to be atomic, array, struct or union, not \""
204                       ~ T.stringof ~ "\"");
205 
206         return false;
207     }
208 }
209 
210 deprecated("Use ocean.meta.traits.Basic.isAggregateType")
211 public template isCompoundType ( T )
212 {
213     static if ( is(T == struct) || is(T == class) || is(T== union) )
214     {
215         static immutable isCompoundType = true;
216     }
217     else
218     {
219         static immutable isCompoundType = false;
220     }
221 }
222 
223 deprecated("Use typeof(T.tupleof)")
224 public template TypeTuple ( T )
225 {
226     static if ( !isCompoundType!(T) )
227     {
228         static assert(false, "TypeTuple!(" ~ T.stringof ~ "): type is not a struct / class / union");
229     }
230 
231     alias typeof(T.tupleof) TypeTuple;
232 }
233 
234 deprecated("Use T.tupleof[i]")
235 public template FieldType ( T, size_t i )
236 {
237     static if ( !isCompoundType!(T) )
238     {
239         static assert(false, "FieldType!(" ~ T.stringof ~ "): type is not a struct / class");
240     }
241 
242     alias typeof (T.tupleof)[i] FieldType;
243 }
244 
245 /*******************************************************************************
246 
247     Gets a pointer to the ith member of a struct/class.
248 
249     Params:
250         i = index of member to get
251         T = type of compound to get member from
252 
253     Params:
254         t = pointer to compound to get member from
255 
256     Returns:
257         pointer to ith member
258 
259 *******************************************************************************/
260 
261 deprecated("Use &t.tupleof[i]")
262 public FieldType!(T, i)* GetField ( size_t i, T ) ( T* t )
263 {
264     return GetField!(i, FieldType!(T, i), T)(t);
265 }
266 
267 /*******************************************************************************
268 
269     Gets a pointer to the ith member of a struct/class.
270 
271     Params:
272         i = index of member to get
273         M = type of member
274         T = type of compound to get member from
275 
276     Params:
277         t = pointer to compound to get member from
278 
279     Returns:
280         pointer to ith member
281 
282 *******************************************************************************/
283 
284 deprecated("Use &t.tupleof[i]")
285 public M* GetField ( size_t i, M, T ) ( T* t )
286 {
287     static if ( !isCompoundType!(T) )
288     {
289         static assert(false, "GetField!(" ~ T.stringof ~ "): type is not a struct / class");
290     }
291 
292     return cast(M*)((cast(void*)t) + T.tupleof[i].offsetof);
293 }
294 
295 /*******************************************************************************
296 
297     Template to get the name of the ith member of a struct / class.
298 
299     Template parameter:
300         i = index of member to get
301         T = type of compound to get member name from
302 
303     Evaluates to:
304         name of the ith member
305 
306 *******************************************************************************/
307 
308 deprecated("Use ocean.meta.codegen.Identifier.fieldIdentifier!(T, i)")
309 public template FieldName ( size_t i, T )
310 {
311     static if ( !isCompoundType!(T) )
312     {
313         static assert(false, "FieldName!(" ~ T.stringof ~ "): type is not a struct / class");
314     }
315 
316     static immutable FieldName = StripFieldName!(T.tupleof[i].stringof);
317 }
318 
319 private template StripFieldName ( istring name, size_t n = size_t.max )
320 {
321     static if ( n >= name.length )
322     {
323         static immutable StripFieldName = StripFieldName!(name, name.length - 1);
324     }
325     else static if ( name[n] == '.' )
326     {
327         static immutable StripFieldName = name[n + 1 .. $];
328     }
329     else static if ( n )
330     {
331         static immutable StripFieldName = StripFieldName!(name, n - 1);
332     }
333     else
334     {
335         static immutable StripFieldName = name;
336     }
337 }
338 
339 deprecated("Use ocean.meta.traits.Aggregates.totalMemberSize")
340 public template SizeofTuple ( Tuple ... )
341 {
342     static if ( Tuple.length > 0 )
343     {
344         static immutable size_t SizeofTuple = Tuple[0].sizeof + SizeofTuple!(Tuple[1..$]);
345     }
346     else
347     {
348         static immutable size_t SizeofTuple = 0;
349     }
350 }
351 
352 deprecated("Use `dst.tupleof[] = src.tupleof[]`")
353 public void copyFields ( T ) ( ref T dst, ref T src )
354 {
355     foreach ( i, t; typeof(dst.tupleof) )
356     {
357         dst.tupleof[i] = src.tupleof[i];
358     }
359 }
360 
361 deprecated("Use `dst.tupleof[] = src.tupleof[]`")
362 public void copyClassFields ( T ) ( T dst, T src )
363 {
364     static assert (is(T == class));
365 
366     foreach ( i, t; typeof(dst.tupleof) )
367     {
368         dst.tupleof[i] = src.tupleof[i];
369     }
370 }
371 
372 deprecated("Either use ocean.meta.traits.Typedef or remove the check")
373 public template isTypedef (T)
374 {
375     static immutable bool isTypedef = false;
376 }
377 
378 deprecated("Either use ocean.meta.traits.Typedef or remove the check")
379 public template StripTypedef (T)
380 {
381     alias T StripTypedef;
382 }
383 
384 deprecated("Use ocean.meta.traits.Indirections.containsDynamicArray")
385 template ContainsDynamicArray ( T ... )
386 {
387     static if (T.length)
388     {
389         static if (isTypedef!(T[0]))
390         {
391             mixin(`
392             static if (is (T[0] Base == typedef))
393             {
394                 // Recurse into typedef.
395 
396                 const ContainsDynamicArray = ContainsDynamicArray!(Base, T[1 .. $]);
397             }
398             `);
399         }
400         else static if (is (T[0] == struct) || is (T[0] == union))
401         {
402             // Recurse into struct/union members.
403 
404             static immutable ContainsDynamicArray = ContainsDynamicArray!(typeof (T[0].tupleof)) ||
405                                          ContainsDynamicArray!(T[1 .. $]);
406         }
407         else
408         {
409             static if (is (T[0] Element : Element[])) // array
410             {
411                 static if (is (Element[] == Unqual!(T[0])))
412                 {
413                     static immutable ContainsDynamicArray = true;
414                 }
415                 else
416                 {
417                     // Static array, recurse into base type.
418 
419                     static immutable ContainsDynamicArray = ContainsDynamicArray!(Element) ||
420                                                  ContainsDynamicArray!(T[1 .. $]);
421                 }
422             }
423             else
424             {
425                 // Skip non-dynamic or static array type.
426 
427                 static immutable ContainsDynamicArray = ContainsDynamicArray!(T[1 .. $]);
428             }
429         }
430     }
431     else
432     {
433         static immutable ContainsDynamicArray = false;
434     }
435 }
436 
437 /*******************************************************************************
438 
439     Evaluates, if T is callable (function, delegate, a class/interface/struct/
440     union implementing opCall() as a member or static method or a typedef of
441     these), to a type tuple with the return type as the first element, followed
442     by the argument types.
443     Evaluates to an empty tuple if T is not callable.
444 
445     Template parameter:
446         T = Type to, if callable, get the return and argument types
447 
448     Evaluates to:
449         a type tuple containing the return and argument types or an empty tuple
450         if T is not callable.
451 
452 *******************************************************************************/
453 
454 deprecated("Use ocean.meta.types.Function.ParametersOf and ReturnTypeOf")
455 template ReturnAndArgumentTypesOf ( T )
456 {
457     static if (isTypedef!(T))
458     {
459         mixin(`
460         static if (is(T F == typedef))
461             alias ReturnAndArgumentTypesOf!(F) ReturnAndArgumentTypesOf;
462         `);
463     }
464     else static if (is(T Args == function) && is(T Return == return))
465     {
466         alias Tuple!(Return, Args) ReturnAndArgumentTypesOf;
467     }
468     else static if (is(T F == delegate) || is(T F == F*) ||
469                     is(typeof(&(T.init.opCall)) F))
470     {
471         alias ReturnAndArgumentTypesOf!(F) ReturnAndArgumentTypesOf;
472     }
473     else
474     {
475         alias Tuple!() ReturnAndArgumentTypesOf;
476     }
477 }
478 
479 import ocean.core.TypeConvert;
480 deprecated("Use ocean.core.TypeConvert.toDg")
481 public alias toDg = ocean.core.TypeConvert.toDg;
482 
483 deprecated("Use ocean.meta.traits.Aggregates.hasMethod")
484 template hasMethod ( T, istring name, Dg )
485 {
486     static assert(is(T == struct) || is(T == class) || is(T == union) ||
487         is(T == interface));
488     static assert(is(Dg == delegate));
489 
490     static if ( is(typeof( { Dg dg = mixin("&T.init." ~ name); } )) )
491     {
492         static immutable bool hasMethod = true;
493     }
494     else
495     {
496         static immutable bool hasMethod = false;
497     }
498 }
499 
500 deprecated("Use ocean.meta.codegen.Identifier.identifier")
501 public template identifier(alias Sym)
502 {
503     static immutable identifier = _identifier!(Sym)();
504 }
505 
506 private istring _identifier(alias Sym)()
507 {
508     static if (is(typeof(Sym) == function))
509     {
510         // Sym.stringof is treated as Sym().stringof
511         // ugly workaround:
512         ParameterTupleOf!(Sym) args;
513         auto name = Sym(args).stringof[];
514         size_t bracketIndex = 0;
515         while (name[bracketIndex] != '(' && bracketIndex < name.length)
516             ++bracketIndex;
517         return name[0 .. bracketIndex];
518     }
519     else
520     {
521         return Sym.stringof;
522     }
523 }
524 
525 deprecated("Use ocean.meta.types.Arrays.ElementTypeOf")
526 public template AAType (T : V[K], V, K)
527 {
528     public alias K Key;
529     public alias V Value;
530 }
531 
532 deprecated("Use ocean.meta.traits.Templates.TemplateInstanceArgs")
533 public template TemplateInstanceArgs (alias Template, Type : Template!(TA), TA...)
534 {
535     public alias TA TemplateInstanceArgs;
536 }
537 
538 deprecated("Use ocean.meta.traits.Arrays.isUTF8StringType")
539 template isStringType( T )
540 {
541     static immutable bool isStringType = is( T : char[] )  ||
542                               is( T : wchar[] ) ||
543                               is( T : dchar[] ) ||
544                               is( T : istring ) ||
545                               is( T : cstring ) ||
546                               is( T : mstring );
547 }
548 
549 deprecated("Use ocean.meta.traits.Basic.isCharType")
550 template isCharType( T )
551 {
552     static immutable bool isCharType =
553         is( Unqual!(T) == char )
554      || is( Unqual!(T) == wchar )
555      || is( Unqual!(T) == dchar );
556 }
557 
558 deprecated("Use ocean.meta.traits.Basic.isSignedIntegerType")
559 template isSignedIntegerType( T )
560 {
561     static immutable bool isSignedIntegerType =
562         is( Unqual!(T) == byte )
563      || is( Unqual!(T) == short )
564      || is( Unqual!(T) == int )
565      || is( Unqual!(T) == long );
566 }
567 
568 deprecated("Use ocean.meta.traits.Basic.isUnsignedIntegerType")
569 template isUnsignedIntegerType( T )
570 {
571     static immutable bool isUnsignedIntegerType =
572         is( Unqual!(T) == ubyte )
573      || is( Unqual!(T) == ushort )
574      || is( Unqual!(T) == uint )
575      || is( Unqual!(T) == ulong );
576 }
577 
578 deprecated("Use ocean.meta.traits.Basic.isIntegerType")
579 template isIntegerType( T )
580 {
581     static immutable bool isIntegerType = isSignedIntegerType!(T) ||
582                                isUnsignedIntegerType!(T);
583 }
584 
585 deprecated("Use ocean.meta.traits.Basic.isRealType")
586 template isRealType( T )
587 {
588     static immutable bool isRealType =
589         is( Unqual!(T) == float )
590      || is( Unqual!(T) == double )
591      || is( Unqual!(T) == real );
592 }
593 
594 deprecated("Use ocean.meta.traits.Basic.isComplexType")
595 template isComplexType( T )
596 {
597     static immutable bool isComplexType =
598         is( Unqual!(T) == cfloat )
599      || is( Unqual!(T) == cdouble )
600      || is( Unqual!(T) == creal );
601 }
602 
603 deprecated("Use ocean.meta.traits.Basic.isImaginaryType")
604 template isImaginaryType( T )
605 {
606     static immutable bool isImaginaryType =
607         is( Unqual!(T) == ifloat )
608      || is( Unqual!(T) == idouble )
609      || is( Unqual!(T) == ireal );
610 }
611 
612 deprecated("Use ocean.meta.traits.Basic.isFloatingPointType")
613 template isFloatingPointType( T )
614 {
615     static immutable bool isFloatingPointType = isRealType!(T)    ||
616                                      isComplexType!(T) ||
617                                      isImaginaryType!(T);
618 }
619 
620 deprecated("Use ocean.meta.traits.Basic.isPointerType")
621 template isPointerType(T)
622 {
623         static immutable isPointerType = false;
624 }
625 
626 deprecated("Use ocean.meta.traits.Basic.isPointerType")
627 template isPointerType(T : T*)
628 {
629         static immutable isPointerType = true;
630 }
631 
632 deprecated("Use ocean.meta.traits.Basic.isReferenceType")
633 template isReferenceType( T )
634 {
635 
636     static immutable bool isReferenceType = isPointerType!(T)  ||
637                                is( T == class )     ||
638                                is( T == interface ) ||
639                                is( T == delegate );
640 }
641 
642 deprecated("Use ocean.meta.traits.Basic.isArrayType")
643 template isDynamicArrayType( T )
644 {
645     static immutable bool isDynamicArrayType = is( typeof(T.init[0])[] == T );
646 }
647 
648 deprecated("Use ocean.meta.traits.Basic.isArrayType")
649 template isStaticArrayType( T : T[U], size_t U )
650 {
651     static immutable bool isStaticArrayType = true;
652 }
653 
654 deprecated("Use ocean.meta.traits.Basic.isArrayType")
655 template isStaticArrayType( T )
656 {
657     static immutable bool isStaticArrayType = false;
658 }
659 
660 deprecated("Use ocean.meta.traits.Basic.isArrayType")
661 template isArrayType(T)
662 {
663     static if (is( T U : U[] ))
664         static immutable bool isArrayType=true;
665     else
666         static immutable bool isArrayType=false;
667 }
668 
669 deprecated("Use ocean.meta.traits.Basic.isArrayType")
670 template isAssocArrayType( T )
671 {
672     static immutable bool isAssocArrayType = is( typeof(T.init.values[0])[typeof(T.init.keys[0])] == T );
673 }
674 
675 deprecated("Use ocean.meta.traits.Basic.isCallableType")
676 template isCallableType( T )
677 {
678     static immutable bool isCallableType = is( T == function )             ||
679                                 is( typeof(*T) == function )    ||
680                                 is( T == delegate )             ||
681                                 is( typeof(T.opCall) == function );
682 }
683 
684 deprecated("Use ocean.meta.types.Function.ReturnTypeOf")
685 template ReturnTypeOf( Fn )
686 {
687     static if (is(Fn Fptr : Fptr*) && is(Fptr == function))
688     {
689         // anything implicitly convertible to function pointer
690         // this also handles new Typedef struct
691         alias ReturnTypeOf!(Fptr) ReturnTypeOf;
692     }
693     else static if( is( Fn Ret == return ) )
694         alias Ret ReturnTypeOf;
695     else
696         static assert( false, "Argument has no return type." );
697 }
698 
699 deprecated("Use ocean.meta.types.Function.ReturnTypeOf")
700 template ReturnTypeOf( alias fn )
701 {
702     alias ReturnTypeOf!(typeof(fn)) ReturnTypeOf;
703 }
704 
705 deprecated("Use ocean.meta.types.Function.ParametersOf")
706 template ParameterTupleOf( Fn )
707 {
708     static if( is( Fn Params == function ) )
709         alias Tuple!(Params) ParameterTupleOf;
710     else static if( is( Fn Params == delegate ) )
711         alias Tuple!(ParameterTupleOf!(Params)) ParameterTupleOf;
712     else static if( is( Fn Params : Params* ) )
713         alias Tuple!(ParameterTupleOf!(Params)) ParameterTupleOf;
714     else
715         static assert( false, "Argument has no parameters." );
716 }
717 
718 deprecated("Use ocean.meta.types.Function.ParametersOf")
719 template ParameterTupleOf( alias fn )
720 {
721     alias ParameterTupleOf!(typeof(fn)) ParameterTupleOf;
722 }
723 
724 deprecated("Use ocean.meta.types.Arrays.StripAllArrays")
725 template BaseTypeOfArrays(T)
726 {
727     static if( is( T S : S[]) ) {
728         alias BaseTypeOfArrays!(S)  BaseTypeOfArrays;
729     }
730     else {
731         alias T BaseTypeOfArrays;
732     }
733 }
734 
735 deprecated("Use ocean.meta.types.Arrays.ElementTypeOf")
736 template ElementTypeOfArray(T:T[])
737 {
738     alias T ElementTypeOfArray;
739 }
740 
741 deprecated("Use ocean.meta.traits.Arrays.rankOfArray")
742 template rankOfArray(T) {
743     static if(is(T S : S[])) {
744         static immutable uint rankOfArray = 1 + rankOfArray!(S);
745     } else {
746         static immutable uint rankOfArray = 0;
747     }
748 }
749 
750 deprecated("Use ocean.meta.codegen.CTFE.toString")
751 istring ctfe_i2a(int i){
752     istring digit="0123456789";
753     istring res;
754     if (i==0){
755         return "0";
756     }
757     bool neg=false;
758     if (i<0){
759         neg=true;
760         i=-i;
761     }
762     while (i>0) {
763         res=digit[i%10]~res;
764         i/=10;
765     }
766     if (neg)
767         return "-"~res;
768     else
769         return res;
770 }
771 
772 deprecated("Use ocean.meta.codegen.CTFE.toString")
773 istring ctfe_i2a(long i){
774     istring digit="0123456789";
775     istring res;
776     if (i==0){
777         return "0";
778     }
779     bool neg=false;
780     if (i<0){
781         neg=true;
782         i=-i;
783     }
784     while (i>0) {
785         res=digit[cast(size_t)(i%10)]~res;
786         i/=10;
787     }
788     if (neg)
789         return '-'~res;
790     else
791         return res;
792 }
793 
794 deprecated("Use ocean.meta.codegen.CTFE.toString")
795 istring ctfe_i2a(uint i){
796     istring digit="0123456789";
797     istring res="";
798     if (i==0){
799         return "0";
800     }
801     bool neg=false;
802     while (i>0) {
803         res=digit[i%10]~res;
804         i/=10;
805     }
806     return res;
807 }
808 
809 deprecated("Use ocean.meta.codegen.CTFE.toString")
810 istring ctfe_i2a(ulong i){
811     istring digit="0123456789";
812     istring res="";
813     if (i==0){
814         return "0";
815     }
816     bool neg=false;
817     while (i>0) {
818         res=digit[cast(size_t)(i%10)]~res;
819         i/=10;
820     }
821     return res;
822 }
823 
824 deprecated("Use ocean.meta.traits.Aggregates.hasMember")
825 public template hasMember(T, istring name)
826 {
827     static assert (
828         is(T == interface) ||
829         is(T == class)     ||
830         is(T == struct)
831     );
832 
833     enum hasMember = __traits(hasMember, T, name);
834 }