1 /++ 2 Various compile-time traits and cleverness. 3 +/ 4 module lu.traits; 5 6 private: 7 8 import std.traits : isArray, isAssociativeArray, isSomeFunction, isType; 9 import std.typecons : Flag, No, Yes; 10 11 public: 12 13 14 // MixinScope 15 /++ 16 The types of scope into which a mixin template may be mixed in. 17 +/ 18 enum MixinScope 19 { 20 function_ = 1 << 0, /// Mixed in inside a function. 21 class_ = 1 << 1, /// Mixed in inside a class. 22 struct_ = 1 << 2, /// Mixed in inside a struct. 23 interface_ = 1 << 3, /// Mixed in inside an interface. 24 union_ = 1 << 4, /// Mixed in inside a union. 25 module_ = 1 << 5, /// Mixed in inside a module. 26 } 27 28 29 // MixinConstraints 30 /++ 31 Mixes in constraints into another mixin template, to provide static 32 guarantees that it is not mixed into a type of scope other than the one specified. 33 34 Using this you can ensure that a mixin template meant to be mixed into a 35 class isn't mixed into a module-level scope, or into a function, etc. 36 37 More than one scope type can be supplied with bitwise OR. 38 39 Example: 40 --- 41 module foo; 42 43 mixin template Foo() 44 { 45 mixin MixinConstraints!(MixinScope.module_, "Foo"); // Constrained to module-level scope 46 } 47 48 mixin Foo; // no problem, scope is MixinScope.module_ 49 50 void bar() 51 { 52 mixin Foo; // static assert(0): scope is MixinScope.function_, not MixinScope.module_ 53 } 54 55 class C 56 { 57 mixin Foo; // static assert(0): ditto but MixinScope.class_ 58 } 59 60 struct C 61 { 62 mixin Foo; // static assert(0): ditto but MixinScope.struct_ 63 } 64 65 mixin template FooStructOrClass() 66 { 67 mixin MixinConstraints(MixinScope.struct_ | MixinScope.class_); 68 } 69 --- 70 71 Params: 72 mixinScope = The scope into which to only allow the mixin to be mixed in. 73 All other kinds of scopes will be statically rejected. 74 mixinName = Optional string name of the mixing-in mixin. 75 Can be anything; it's just used for the error messages. 76 +/ 77 mixin template MixinConstraints(MixinScope mixinScope, string mixinName = "a constrained mixin") 78 { 79 private: 80 import lu.traits : CategoryName, MixinScope; 81 import std.traits : fullyQualifiedName, isSomeFunction; 82 83 // https://forum.dlang.org/post/sk4hqm$12cf$1@digitalmars.com 84 alias mixinParent = __traits(parent, {}); 85 86 static if (isSomeFunction!mixinParent) 87 { 88 static if (!(mixinScope & MixinScope.function_)) 89 { 90 import std.format : format; 91 alias mixinParentInfo = CategoryName!mixinParent; 92 static assert(0, ("%s `%s` mixes in `%s` but it is not supposed to be " ~ 93 "mixed into a function") 94 .format(mixinParentInfo.type, mixinParentInfo.fqn, mixinName)); 95 } 96 } 97 else static if (is(mixinParent == class)) 98 { 99 static if (!(mixinScope & MixinScope.class_)) 100 { 101 import std.format : format; 102 alias mixinParentInfo = CategoryName!mixinParent; 103 static assert(0, ("%s `%s` mixes in `%s` but it is not supposed to be " ~ 104 "mixed into a class") 105 .format(mixinParentInfo.type, mixinParentInfo.fqn, mixinName)); 106 } 107 } 108 else static if (is(mixinParent == struct)) 109 { 110 static if (!(mixinScope & MixinScope.struct_)) 111 { 112 import std.format : format; 113 alias mixinParentInfo = CategoryName!mixinParent; 114 static assert(0, ("%s `%s` mixes in `%s` but it is not supposed to be " ~ 115 "mixed into a struct") 116 .format(mixinParentInfo.type, mixinParentInfo.fqn, mixinName)); 117 } 118 } 119 else static if (is(mixinParent == interface)) 120 { 121 static if (!(mixinScope & MixinScope.interface_)) 122 { 123 import std.format : format; 124 alias mixinParentInfo = CategoryName!mixinParent; 125 static assert(0, ("%s `%s` mixes in `%s` but it is not supposed to be " ~ 126 "mixed into an interface") 127 .format(mixinParentInfo.type, mixinParentInfo.fqn, mixinName)); 128 } 129 } 130 else static if (is(mixinParent == union)) 131 { 132 static if (!(mixinScope & MixinScope.union_)) 133 { 134 import std.format : format; 135 alias mixinParentInfo = CategoryName!mixinParent; 136 static assert(0, ("%s `%s` mixes in `%s` but it is not supposed to be " ~ 137 "mixed into a union") 138 .format(mixinParentInfo.type, mixinParentInfo.fqn, mixinName)); 139 } 140 } 141 else static if (((__VERSION__ >= 2087L) && __traits(isModule, mixinParent)) || 142 ((__VERSION__ < 2087L) && 143 __traits(compiles, { mixin("import ", fullyQualifiedName!mixinParent, ";"); }))) 144 { 145 static if (!(mixinScope & MixinScope.module_)) 146 { 147 import std.format : format; 148 alias mixinParentInfo = CategoryName!mixinParent; 149 static assert(0, ("%s `%s` mixes in `%s` but it is not supposed to be " ~ 150 "mixed into a module-level scope") 151 .format(mixinParentInfo.type, mixinParentInfo.fqn, mixinName)); 152 } 153 } 154 else 155 { 156 import std.format : format; 157 static assert(0, "Logic error; unexpected scope type of parent of mixin `%s`: `%s`" 158 .format(mixinName, fullyQualifiedName!mixinParent)); 159 } 160 } 161 162 /// 163 unittest 164 { 165 void fun() 166 { 167 // MixinConstraints!(MixinScope.function_, "TestMixinConstrainedToFunctions"); 168 mixin TestMixinConstrainedToFunctions; 169 } 170 171 class TestClassC 172 { 173 // MixinConstraints!(MixinScope.class_, "TestMixinConstrainedToClass"); 174 mixin TestMixinConstrainedToClass; 175 } 176 177 struct TestStructS 178 { 179 // mixin MixinConstraints!(MixinScope.struct_, "TestMixinConstrainedToStruct"); 180 mixin TestMixinConstrainedToStruct; 181 } 182 183 struct TestStructS2 184 { 185 mixin TestMixinConstrainedToClassOrStruct; 186 } 187 } 188 189 version(unittest) 190 { 191 mixin template TestMixinConstrainedToFunctions() 192 { 193 mixin MixinConstraints!(MixinScope.function_, "TestMixinConstrainedToFunctions"); 194 } 195 196 mixin template TestMixinConstrainedToClass() 197 { 198 mixin MixinConstraints!(MixinScope.class_, "TestMixinConstrainedToClass"); 199 } 200 201 mixin template TestMixinConstrainedToStruct() 202 { 203 mixin MixinConstraints!(MixinScope.struct_, "TestMixinConstrainedToStruct"); 204 } 205 206 mixin template TestMixinConstrainedToClassOrStruct() 207 { 208 mixin MixinConstraints!((MixinScope.class_ | MixinScope.struct_), 209 "TestMixinConstrainedToClassOrStruct"); 210 } 211 212 mixin template TestMixinConstrainedToModule() 213 { 214 mixin MixinConstraints!((MixinScope.module_), 215 "TestMixinConstrainedToModule"); 216 } 217 218 mixin TestMixinConstrainedToModule; 219 } 220 221 222 // CategoryName 223 /++ 224 Provides string representations of the category of a symbol, where such is not 225 a fundamental primitive variable but a module, a function, a delegate, 226 a class or a struct. 227 228 Accurate module detection only works on compilers 2.087 and later, due to 229 missing support for `__traits(isModule)`. 230 231 Example: 232 --- 233 module foo; 234 235 void bar() {} 236 237 alias categoryName = CategoryName!bar; 238 239 assert(categoryName.type == "function"); 240 assert(categoryName.name == "bar"); 241 assert(categoryName.fqn == "foo.bar"); 242 --- 243 244 Params: 245 sym = Symbol to provide the strings for. 246 +/ 247 template CategoryName(alias sym) 248 { 249 import std.traits : isDelegate, isFunction, fullyQualifiedName; 250 251 // type 252 /++ 253 String representation of the category type of `sym`. 254 +/ 255 static if (isFunction!sym) 256 { 257 enum type = "function"; 258 } 259 else static if (isDelegate!sym) 260 { 261 enum type = "delegate"; 262 } 263 else static if (is(sym == class) || is(typeof(sym) == class)) 264 { 265 enum type = "class"; 266 } 267 else static if (is(sym == struct) || is(typeof(sym) == struct)) 268 { 269 enum type = "struct"; 270 } 271 else static if (is(sym == interface) || is(typeof(sym) == interface)) 272 { 273 enum type = "interface"; 274 } 275 else static if (is(sym == union) || is(typeof(sym) == union)) 276 { 277 enum type = "union"; 278 } 279 else static if ( 280 ((__VERSION__ >= 2087L) && __traits(isModule, sym)) || 281 ((__VERSION__ < 2087L) && 282 __traits(compiles, { mixin("import ", fullyQualifiedName!sym, ";"); }))) 283 { 284 enum type = "module"; 285 } 286 else 287 { 288 // Not quite sure when this could happen 289 enum type = "some"; 290 } 291 292 293 // name 294 /++ 295 A short name for the symbol `sym` is an alias of. 296 +/ 297 enum name = __traits(identifier, sym); 298 299 300 // fqn 301 /++ 302 The fully qualified name for the symbol `sym` is an alias of. 303 +/ 304 enum fqn = fullyQualifiedName!sym; 305 } 306 307 /// 308 unittest 309 { 310 bool localSymbol; 311 312 void fn() {} 313 314 auto dg = () => localSymbol; 315 316 class C {} 317 C c; 318 319 struct S {} 320 S s; 321 322 interface I {} 323 324 union U 325 { 326 int i; 327 bool b; 328 } 329 330 U u; 331 332 alias Ffn = CategoryName!fn; 333 static assert(Ffn.type == "function"); 334 static assert(Ffn.name == "fn"); 335 // Can't test fqn from inside a unittest 336 337 alias Fdg = CategoryName!dg; 338 static assert(Fdg.type == "delegate"); 339 static assert(Fdg.name == "dg"); 340 // Ditto 341 342 alias Fc = CategoryName!c; 343 static assert(Fc.type == "class"); 344 static assert(Fc.name == "c"); 345 // Ditto 346 347 alias Fs = CategoryName!s; 348 static assert(Fs.type == "struct"); 349 static assert(Fs.name == "s"); 350 351 alias Fm = CategoryName!(lu.traits); 352 static assert(Fm.type == "module"); 353 static assert(Fm.name == "traits"); 354 static assert(Fm.fqn == "lu.traits"); 355 356 alias Fi = CategoryName!I; 357 static assert(Fi.type == "interface"); 358 static assert(Fi.name == "I"); 359 360 alias Fu = CategoryName!u; 361 static assert(Fu.type == "union"); 362 static assert(Fu.name == "u"); 363 } 364 365 366 // TakesParams 367 /++ 368 Given a function and a tuple of types, evaluates whether that function could 369 be called with that tuple as parameters. Alias version (works on functions, 370 not function types.) 371 372 Qualifiers like `const` and`immutable` are skipped, which may make it a poor 373 choice if dealing with functions that require such arguments. 374 375 It is merely syntactic sugar, using [std.meta] and [std.traits] behind the scenes. 376 377 Example: 378 --- 379 void noParams(); 380 bool boolParam(bool); 381 string stringParam(string); 382 float floatParam(float); 383 384 static assert(TakesParams!(noParams)); 385 static assert(TakesParams!(boolParam, bool)); 386 static assert(TakesParams!(stringParam, string)); 387 static assert(TakesParams!(floatParam, float)); 388 --- 389 390 Params: 391 fun = Function to evaluate the parameters of. 392 P = Variadic list of types to compare `fun`'s function parameters with. 393 +/ 394 template TakesParams(alias fun, P...) 395 if (isSomeFunction!fun) 396 { 397 import std.traits : Parameters, Unqual, staticMap; 398 399 alias FunParams = staticMap!(Unqual, Parameters!fun); 400 alias PassedParams = staticMap!(Unqual, P); 401 402 static if (is(FunParams : PassedParams)) 403 { 404 enum TakesParams = true; 405 } 406 else 407 { 408 enum TakesParams = false; 409 } 410 } 411 412 /// 413 unittest 414 { 415 void foo(); 416 void foo1(string); 417 void foo2(string, int); 418 void foo3(bool, bool, bool); 419 420 static assert(TakesParams!(foo));//, AliasSeq!())); 421 static assert(TakesParams!(foo1, string)); 422 static assert(TakesParams!(foo2, string, int)); 423 static assert(TakesParams!(foo3, bool, bool, bool)); 424 425 static assert(!TakesParams!(foo, string)); 426 static assert(!TakesParams!(foo1, string, int)); 427 static assert(!TakesParams!(foo2, bool, bool, bool)); 428 } 429 430 431 // TakesParams 432 /++ 433 Given a function and a tuple of types, evaluates whether that function could 434 be called with that tuple as parameters. Non-alias version (works on types). 435 436 Qualifiers like `const` and `immutable` are skipped, which may make it a 437 poor choice if dealing with functions that require such arguments. 438 439 It is merely syntactic sugar, using [std.meta] and [std.traits] behind the scenes. 440 441 Example: 442 --- 443 void noParams(); 444 bool boolParam(bool); 445 string stringParam(string); 446 float floatParam(float); 447 448 alias N = typeof(noParams); 449 alias B = typeof(boolParam); 450 alias S = typeof(stringParam); 451 alias F = typeof(floatParam); 452 453 static assert(TakesParams!N); 454 static assert(TakesParams!(B, bool)); 455 static assert(TakesParams!(S, string)); 456 static assert(TakesParams!(F, float)); 457 --- 458 459 Params: 460 Fun = Type of function to evaluate the parameters of. 461 P = Variadic list of types to compare `Fun` function parameters with. 462 +/ 463 template TakesParams(Fun, P...) 464 if (isSomeFunction!Fun) 465 { 466 import std.traits : Parameters, Unqual, staticMap; 467 468 alias FunParams = staticMap!(Unqual, Parameters!Fun); 469 alias PassedParams = staticMap!(Unqual, P); 470 471 static if (is(FunParams : PassedParams)) 472 { 473 enum TakesParams = true; 474 } 475 else 476 { 477 enum TakesParams = false; 478 } 479 } 480 481 /// 482 unittest 483 { 484 void foo(); 485 void foo1(string); 486 void foo2(string, int); 487 void foo3(bool, bool, bool); 488 489 alias F = typeof(foo); 490 alias F1 = typeof(foo1); 491 alias F2 = typeof(foo2); 492 alias F3 = typeof(foo3); 493 494 static assert(TakesParams!F);//, AliasSeq!())); 495 static assert(TakesParams!(F1, string)); 496 static assert(TakesParams!(F2, string, int)); 497 static assert(TakesParams!(F3, bool, bool, bool)); 498 499 static assert(!TakesParams!(F, string)); 500 static assert(!TakesParams!(F1, string, int)); 501 static assert(!TakesParams!(F2, bool, bool, bool)); 502 } 503 504 505 // isSerialisable 506 /++ 507 Eponymous template bool of whether a variable can be treated as a mutable 508 variable, like a fundamental integral, and thus be serialised. 509 510 Currently it does not support static arrays. 511 512 Params: 513 sym = Alias of symbol to introspect. 514 +/ 515 template isSerialisable(alias sym) 516 { 517 import std.traits : isType; 518 519 static if (!isType!sym) 520 { 521 import std.traits : isSomeFunction; 522 523 alias T = typeof(sym); 524 525 enum isSerialisable = 526 !isSomeFunction!T && 527 !__traits(isTemplate, T) && 528 //!__traits(isAssociativeArray, T) && 529 !__traits(isStaticArray, T); 530 } 531 else 532 { 533 enum isSerialisable = false; 534 } 535 } 536 537 /// 538 unittest 539 { 540 int i; 541 char[] c; 542 char[8] c2; 543 struct S {} 544 class C {} 545 enum E { foo } 546 E e; 547 548 static assert(isSerialisable!i); 549 static assert(isSerialisable!c); 550 static assert(!isSerialisable!c2); // should static arrays pass? 551 static assert(!isSerialisable!S); 552 static assert(!isSerialisable!C); 553 static assert(!isSerialisable!E); 554 static assert(isSerialisable!e); 555 } 556 557 558 // isTrulyString 559 /++ 560 True if a type is `string`, `dstring` or `wstring`; otherwise false. 561 562 Does not consider e.g. `char[]` a string, as 563 [std.traits.isSomeString|isSomeString] does. 564 565 Params: 566 S = String type to introspect. 567 +/ 568 enum isTrulyString(S) = is(S == string) || is(S == dstring) || is(S == wstring); 569 570 /// 571 unittest 572 { 573 static assert(isTrulyString!string); 574 static assert(isTrulyString!dstring); 575 static assert(isTrulyString!wstring); 576 static assert(!isTrulyString!(char[])); 577 static assert(!isTrulyString!(dchar[])); 578 static assert(!isTrulyString!(wchar[])); 579 } 580 581 582 // isMerelyArray 583 /++ 584 True if a type is a non-string array; otherwise false. 585 586 For now also evaluates to true for static arrays. 587 588 Params: 589 S = Array type to introspect. 590 +/ 591 enum isMerelyArray(S) = isArray!S && !isTrulyString!S; 592 593 /// 594 unittest 595 { 596 static assert(!isMerelyArray!string); 597 static assert(!isMerelyArray!dstring); 598 static assert(!isMerelyArray!wstring); 599 static assert(isMerelyArray!(char[])); 600 static assert(isMerelyArray!(dchar[])); 601 static assert(isMerelyArray!(wchar[])); 602 static assert(isMerelyArray!(int[5])); 603 } 604 605 606 // UnqualArray 607 /++ 608 Given an array of qualified elements, aliases itself to one such of 609 unqualified elements. 610 611 Params: 612 QualArray = Qualified array type. 613 QualType = Qualified type, element of `QualArray`. 614 +/ 615 template UnqualArray(QualArray : QualType[], QualType) 616 if (!isAssociativeArray!QualType) 617 { 618 import std.traits : Unqual; 619 620 alias UnqualArray = Unqual!QualType[]; 621 } 622 623 /// 624 unittest 625 { 626 alias ConstStrings = const(string)[]; 627 alias UnqualStrings = UnqualArray!ConstStrings; 628 static assert(is(UnqualStrings == string[])); 629 630 alias ImmChars = string; 631 alias UnqualChars = UnqualArray!ImmChars; 632 static assert(is(UnqualChars == char[])); 633 634 alias InoutBools = inout(bool)[]; 635 alias UnqualBools = UnqualArray!InoutBools; 636 static assert(is(UnqualBools == bool[])); 637 638 alias ConstChars = const(char)[]; 639 alias UnqualChars2 = UnqualArray!ConstChars; 640 static assert(is(UnqualChars2 == char[])); 641 } 642 643 644 // UnqualArray 645 /++ 646 Given an associative array with elements that have a storage class, aliases 647 itself to an associative array with elements without the storage classes. 648 649 Params: 650 QualArray = Qualified associative array type. 651 QualElem = Qualified type, element of `QualArray`. 652 QualKey = Qualified type, key of `QualArray`. 653 +/ 654 template UnqualArray(QualArray : QualElem[QualKey], QualElem, QualKey) 655 if (!isArray!QualElem) 656 { 657 import std.traits : Unqual; 658 659 alias UnqualArray = Unqual!QualElem[Unqual!QualKey]; 660 } 661 662 /// 663 unittest 664 { 665 alias ConstStringAA = const(string)[int]; 666 alias UnqualStringAA = UnqualArray!ConstStringAA; 667 static assert (is(UnqualStringAA == string[int])); 668 669 alias ImmIntAA = immutable(int)[char]; 670 alias UnqualIntAA = UnqualArray!ImmIntAA; 671 static assert(is(UnqualIntAA == int[char])); 672 673 alias InoutBoolAA = inout(bool)[long]; 674 alias UnqualBoolAA = UnqualArray!InoutBoolAA; 675 static assert(is(UnqualBoolAA == bool[long])); 676 677 alias ConstCharAA = const(char)[string]; 678 alias UnqualCharAA = UnqualArray!ConstCharAA; 679 static assert(is(UnqualCharAA == char[string])); 680 } 681 682 683 // UnqualArray 684 /++ 685 Given an associative array of arrays with a storage class, aliases itself to 686 an associative array with array elements without the storage classes. 687 688 Params: 689 QualArray = Qualified associative array type. 690 QualElem = Qualified type, element of `QualArray`. 691 QualKey = Qualified type, key of `QualArray`. 692 +/ 693 template UnqualArray(QualArray : QualElem[QualKey], QualElem, QualKey) 694 if (isArray!QualElem) 695 { 696 import std.traits : Unqual; 697 698 static if (isTrulyString!(Unqual!QualElem)) 699 { 700 alias UnqualArray = Unqual!QualElem[Unqual!QualKey]; 701 } 702 else 703 { 704 alias UnqualArray = UnqualArray!QualElem[Unqual!QualKey]; 705 } 706 } 707 708 /// 709 unittest 710 { 711 alias ConstStringArrays = const(string[])[int]; 712 alias UnqualStringArrays = UnqualArray!ConstStringArrays; 713 static assert (is(UnqualStringArrays == string[][int])); 714 715 alias ImmIntArrays = immutable(int[])[char]; 716 alias UnqualIntArrays = UnqualArray!ImmIntArrays; 717 static assert(is(UnqualIntArrays == int[][char])); 718 719 alias InoutBoolArrays = inout(bool)[][long]; 720 alias UnqualBoolArrays = UnqualArray!InoutBoolArrays; 721 static assert(is(UnqualBoolArrays == bool[][long])); 722 723 alias ConstCharArrays = const(char)[][string]; 724 alias UnqualCharArrays = UnqualArray!ConstCharArrays; 725 static assert(is(UnqualCharArrays == char[][string])); 726 } 727 728 729 // isStruct 730 /++ 731 Eponymous template that is true if the passed type is a struct. 732 733 Used with [std.meta.Filter], which cannot take `is()` expressions. 734 735 Params: 736 T = Type to introspect. 737 +/ 738 enum isStruct(T) = is(T == struct); 739 740 741 // stringofParams 742 /++ 743 Produces a string of the unqualified parameters of the passed function alias. 744 745 Example: 746 --- 747 void foo(bool b, int i, string s) {} 748 static assert(stringofParams!foo == "bool, int, string"); 749 --- 750 751 Params: 752 fun = A function alias to get the parameter string of. 753 +/ 754 template stringofParams(alias fun) 755 { 756 import std.traits : Parameters, Unqual, staticMap; 757 758 alias FunParams = staticMap!(Unqual, staticMap!(Unqual, Parameters!fun)); 759 enum stringofParams = FunParams.stringof[1..$-1]; 760 } 761 762 /// 763 unittest 764 { 765 void foo(); 766 void foo1(string); 767 void foo2(string, int); 768 void foo3(bool, bool, bool); 769 770 enum ofFoo = stringofParams!foo; 771 enum ofFoo1 = stringofParams!foo1; 772 enum ofFoo2 = stringofParams!foo2; 773 enum ofFoo3 = stringofParams!foo3; 774 775 static assert(!ofFoo.length, ofFoo); 776 static assert((ofFoo1 == "string"), ofFoo1); 777 static assert((ofFoo2 == "string, int"), ofFoo2); 778 static assert((ofFoo3 == "bool, bool, bool"), ofFoo3); 779 } 780 781 782 static if ((__VERSION__ == 2088L) || (__VERSION__ == 2089L)) 783 { 784 // getSymbolsByUDA 785 /++ 786 Provide a non-2.088, non-2.089 [std.traits.getSymbolsByUDA|getSymbolsByUDA]. 787 788 The [std.traits.getSymbolsByUDA|getSymbolsByUDA] in 2.088/2.089 is 789 completely broken by having inserted a constraint to force it to only 790 work on aggregates, which a module apparently isn't. 791 +/ 792 template getSymbolsByUDA(alias symbol, alias attribute) 793 //if (isAggregateType!symbol) // <-- 794 { 795 import std.traits : hasUDA; 796 797 alias membersWithUDA = getSymbolsByUDAImpl!(symbol, attribute, __traits(allMembers, symbol)); 798 799 // if the symbol itself has the UDA, tack it on to the front of the list 800 static if (hasUDA!(symbol, attribute)) 801 { 802 alias getSymbolsByUDA = AliasSeq!(symbol, membersWithUDA); 803 } 804 else 805 { 806 alias getSymbolsByUDA = membersWithUDA; 807 } 808 } 809 810 811 // getSymbolsByUDAImpl 812 /++ 813 Implementation of [std.traits.getSymbolsByUDA|getSymbolsByUDA], copy/pasted. 814 +/ 815 private template getSymbolsByUDAImpl(alias symbol, alias attribute, names...) 816 { 817 import std.meta : AliasSeq; 818 819 static if (names.length == 0) 820 { 821 alias getSymbolsByUDAImpl = AliasSeq!(); 822 } 823 else 824 { 825 alias tail = getSymbolsByUDAImpl!(symbol, attribute, names[1 .. $]); 826 827 // Filtering inaccessible members. 828 static if (!__traits(compiles, __traits(getMember, symbol, names[0]))) 829 { 830 alias getSymbolsByUDAImpl = tail; 831 } 832 else 833 { 834 import std.traits : hasUDA, isFunction; 835 836 alias member = __traits(getMember, symbol, names[0]); 837 838 // Filtering not compiled members such as alias of basic types. 839 static if (!__traits(compiles, hasUDA!(member, attribute))) 840 { 841 alias getSymbolsByUDAImpl = tail; 842 } 843 // Get overloads for functions, in case different overloads have different sets of UDAs. 844 else static if (isFunction!member) 845 { 846 import std.meta : AliasSeq, Filter; 847 848 enum hasSpecificUDA(alias member) = hasUDA!(member, attribute); 849 alias overloadsWithUDA = Filter!(hasSpecificUDA, __traits(getOverloads, symbol, names[0])); 850 alias getSymbolsByUDAImpl = AliasSeq!(overloadsWithUDA, tail); 851 } 852 else static if (hasUDA!(member, attribute)) 853 { 854 alias getSymbolsByUDAImpl = AliasSeq!(member, tail); 855 } 856 else 857 { 858 alias getSymbolsByUDAImpl = tail; 859 } 860 } 861 } 862 } 863 } 864 else 865 { 866 // Merely forward to the real template. 867 public import std.traits : getSymbolsByUDA; 868 } 869 870 871 // isMutableArrayOfImmutables 872 /++ 873 Evaluates whether or not a passed array type is a mutable array of immutable 874 elements, such as a string. 875 876 Params: 877 Array = Array to inspect. 878 +/ 879 enum isMutableArrayOfImmutables(Array : Element[], Element) = 880 !is(Array == immutable) && is(Element == immutable); 881 882 /// 883 unittest 884 { 885 static assert(isMutableArrayOfImmutables!string); 886 static assert(isMutableArrayOfImmutables!wstring); 887 static assert(isMutableArrayOfImmutables!dstring); 888 static assert(!isMutableArrayOfImmutables!(immutable(string))); 889 890 static assert(isMutableArrayOfImmutables!(immutable(int)[])); 891 static assert(!isMutableArrayOfImmutables!(immutable(int[]))); 892 }