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 }