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