1 /++ 2 Base reflection utilities. 3 4 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 5 Authors: Ilia Ki 6 Macros: 7 +/ 8 module mir.reflection; 9 10 import std.meta; 11 import std.traits: Parameters, isSomeFunction, FunctionAttribute, functionAttributes, EnumMembers, isAggregateType; 12 import mir.internal.meta: hasUDA, getUDAs; 13 import mir.functional: Tuple; 14 import mir.exception: toMutable; 15 16 deprecated 17 package alias isSomeStruct = isAggregateType; 18 19 /++ 20 Attribute to force member serialization for static fields, compiletime `enum` members and non-property methods. 21 +/ 22 enum reflectSerde; 23 24 /++ 25 Match types like `std.typeconst: Nullable`. 26 +/ 27 template isStdNullable(T) 28 { 29 import std.traits : hasMember; 30 31 private __gshared T* aggregate; 32 33 enum bool isStdNullable = 34 hasMember!(T, "isNull") && 35 hasMember!(T, "get") && 36 hasMember!(T, "nullify") && 37 is(typeof(__traits(getMember, aggregate, "isNull")()) == bool) && 38 !is(typeof(__traits(getMember, aggregate, "get")()) == void) && 39 is(typeof(__traits(getMember, aggregate, "nullify")()) == void); 40 } 41 42 /// 43 version(mir_core_test) unittest 44 { 45 import std.typecons; 46 static assert(isStdNullable!(Nullable!double)); 47 } 48 49 /// 50 version(mir_core_test) unittest 51 { 52 import mir.algebraic; 53 static assert(isStdNullable!(Nullable!double)); 54 } 55 56 /// Attribute for deprecated API 57 struct reflectDeprecated(string target) 58 { 59 /// 60 string msg; 61 62 /++ 63 Number in an issue tracker. Not mandatory. 64 +/ 65 uint issueNumber = uint.max; 66 /++ 67 Should be kind of version number if one can be given. 68 Can be something else if that's not possible. Not mandatory. 69 +/ 70 string removalTime; 71 } 72 73 /// Attribute to rename methods, types and functions 74 template ReflectName(string target) 75 { 76 /// 77 struct ReflectName(Args...) 78 { 79 /// 80 string name; 81 } 82 } 83 84 /// ditto 85 template reflectName(string target = null, Args...) 86 { 87 /// 88 auto reflectName(string name) 89 { 90 alias TargetName = ReflectName!target; 91 return TargetName!Args(name); 92 } 93 } 94 95 /// 96 version(mir_core_test) unittest 97 { 98 enum E { A, B, C } 99 100 struct S 101 { 102 @reflectName("A") 103 int a; 104 105 @reflectName!"c++"("B") 106 int b; 107 108 @reflectName!("C", double)("cd") 109 @reflectName!("C", float)("cf") 110 F c(F)() 111 { 112 return b; 113 } 114 } 115 116 import std.traits: hasUDA; 117 118 alias UniName = ReflectName!null; 119 alias CppName = ReflectName!"c++"; 120 alias CName = ReflectName!"C"; 121 122 static assert(hasUDA!(S.a, UniName!()("A"))); 123 static assert(hasUDA!(S.b, CppName!()("B"))); 124 125 // static assert(hasUDA!(S.c, ReflectName)); // doesn't work for now 126 static assert(hasUDA!(S.c, CName)); 127 static assert(hasUDA!(S.c, CName!double)); 128 static assert(hasUDA!(S.c, CName!float)); 129 static assert(hasUDA!(S.c, CName!double("cd"))); 130 static assert(hasUDA!(S.c, CName!float("cf"))); 131 } 132 133 /// Attribute to rename methods, types and functions 134 template ReflectMeta(string target, string[] fields) 135 { 136 /// 137 struct ReflectMeta(Args...) 138 { 139 /// 140 Args args; 141 static foreach(i, field; fields) 142 mixin(`alias ` ~ field ~` = args[` ~ i.stringof ~`];`); 143 } 144 } 145 146 /// ditto 147 template reflectMeta(string target, string[] fields) 148 { 149 /// 150 auto reflectMeta(Args...)(Args args) 151 if (args.length <= fields.length) 152 { 153 alias TargetMeta = ReflectMeta!(target, fields); 154 return TargetMeta!Args(args); 155 } 156 } 157 158 /// 159 version(mir_core_test) unittest 160 { 161 enum E { A, B, C } 162 163 struct S 164 { 165 int a; 166 @reflectMeta!("c++", ["type"])(E.C) 167 int b; 168 } 169 170 import std.traits: hasUDA; 171 172 alias CppMeta = ReflectMeta!("c++", ["type"]); 173 174 static assert(CppMeta!E(E.C).type == E.C); 175 static assert(!hasUDA!(S.a, CppMeta!E(E.A))); 176 static assert(hasUDA!(S.b, CppMeta!E(E.C))); 177 } 178 179 /++ 180 Attribute to ignore a reflection target 181 +/ 182 template reflectIgnore(string target) 183 { 184 enum reflectIgnore; 185 } 186 187 /// 188 version(mir_core_test) unittest 189 { 190 struct S 191 { 192 @reflectIgnore!"c++" 193 int a; 194 } 195 196 import std.traits: hasUDA; 197 static assert(hasUDA!(S.a, reflectIgnore!"c++")); 198 } 199 200 /// Attribute for documentation and unittests 201 struct ReflectDoc(string target) 202 { 203 /// 204 string text; 205 /// 206 reflectUnittest!target test; 207 208 /// 209 @safe pure nothrow @nogc 210 this(string text) 211 { 212 this.text = text; 213 } 214 215 /// 216 @safe pure nothrow @nogc 217 this(string text, reflectUnittest!target test) 218 { 219 this.text = text; 220 this.test = test; 221 } 222 223 /// 224 void toString(W)(scope ref W w) scope const 225 { 226 w.put(cast()this.text); 227 228 if (this.test.text.length) 229 { 230 w.put("\nExample usage:\n"); 231 w.put(cast()this.test.text); 232 } 233 } 234 235 /// 236 @safe pure nothrow 237 string toString()() scope const 238 { 239 return this.text ~ "\nExample usage:\n" ~ this.test.text; 240 } 241 } 242 243 /++ 244 Attribute for documentation. 245 +/ 246 template reflectDoc(string target = null) 247 { 248 /// 249 ReflectDoc!target reflectDoc(string text) 250 { 251 return ReflectDoc!target(text); 252 } 253 254 /// 255 ReflectDoc!target reflectDoc(string text, reflectUnittest!target test) 256 { 257 return ReflectDoc!target(text, test); 258 } 259 } 260 261 /++ 262 +/ 263 template reflectGetDocs(string target, alias symbol) 264 { 265 static if (hasUDA!(symbol, ReflectDoc!target)) 266 static immutable(ReflectDoc!target[]) reflectGetDocs = [getUDAs!(symbol, ReflectDoc!target)]; 267 else 268 static immutable(ReflectDoc!target[]) reflectGetDocs = null; 269 } 270 271 /// ditto 272 template reflectGetDocs(string target) 273 { 274 /// 275 alias reflectGetDocs(alias symbol) = .reflectGetDocs!(target, symbol); 276 277 /// ditto 278 immutable(ReflectDoc!target)[] reflectGetDocs(T)(T value) 279 @safe pure nothrow @nogc 280 if (is(T == enum)) 281 { 282 foreach (i, member; EnumMembers!T) 283 {{ 284 alias all = __traits(getAttributes, EnumMembers!T[i]); 285 }} 286 static immutable ReflectDoc!target[][EnumMembers!T.length] docs = [staticMap!(reflectGetDocs, EnumMembers!T)]; 287 import mir.enums: getEnumIndex; 288 uint index = void; 289 if (getEnumIndex(value, index)) 290 return docs[index]; 291 assert(0); 292 } 293 } 294 295 /// 296 version(mir_core_test) unittest 297 { 298 enum E 299 { 300 @reflectDoc("alpha") 301 a, 302 @reflectDoc!"C#"("Beta", reflectUnittest!"C#"("some c# code")) 303 @reflectDoc("beta") 304 b, 305 c, 306 } 307 308 alias Doc = ReflectDoc!null; 309 alias CSDoc = ReflectDoc!"C#"; 310 311 static assert(reflectGetDocs!null(E.a) == [Doc("alpha")]); 312 static assert(reflectGetDocs!"C#"(E.b) == [CSDoc("Beta", reflectUnittest!"C#"("some c# code"))]); 313 static assert(reflectGetDocs!null(E.b) == [Doc("beta")]); 314 static assert(reflectGetDocs!null(E.c) is null); 315 316 struct S 317 { 318 @reflectDoc("alpha") 319 @reflectDoc!"C#"("Alpha") 320 int a; 321 } 322 323 static assert(reflectGetDocs!(null, S.a) == [Doc("alpha")]); 324 static assert(reflectGetDocs!("C#", S.a) == [CSDoc("Alpha")]); 325 326 import std.conv: to; 327 static assert(CSDoc("Beta", reflectUnittest!"C#"("some c# code")).to!string == "Beta\nExample usage:\nsome c# code"); 328 } 329 330 /++ 331 Attribute for extern unit-test. 332 +/ 333 struct reflectUnittest(string target) 334 { 335 /// 336 string text; 337 338 @safe pure nothrow @nogc: 339 340 this(string text) 341 { 342 this.text = text; 343 } 344 345 this(const typeof(this) other) 346 { 347 this.text = other.text; 348 } 349 } 350 351 /++ 352 +/ 353 template reflectGetUnittest(string target, alias symbol) 354 { 355 static if (hasUDA!(symbol, reflectUnittest!target)) 356 enum string reflectGetUnittest = getUDA!(symbol, reflectUnittest).text; 357 else 358 enum string reflectGetUnittest = null; 359 } 360 361 /// ditto 362 template reflectGetUnittest(string target) 363 { 364 /// 365 alias reflectGetUnittest(alias symbol) = .reflectGetUnittest!(target, symbol); 366 367 /// 368 string reflectGetUnittest(T)(T value) 369 if (is(T == enum)) 370 { 371 foreach (i, member; EnumMembers!T) 372 {{ 373 alias all = __traits(getAttributes, EnumMembers!T[i]); 374 }} 375 static immutable string[EnumMembers!T.length] tests = [staticMap!(reflectGetUnittest, EnumMembers!T)]; 376 import mir.enums: getEnumIndex; 377 uint index = void; 378 if (getEnumIndex(value, index)) 379 return tests[index]; 380 assert(0); 381 } 382 } 383 384 /// 385 version(mir_core_test) unittest 386 { 387 enum E 388 { 389 @reflectUnittest!"c++"("assert(E::a == 0);") 390 a, 391 @reflectUnittest!"c++"("assert(E::b == 1);") 392 b, 393 c, 394 } 395 396 static assert(reflectGetUnittest!"c++"(E.a) == "assert(E::a == 0);"); 397 static assert(reflectGetUnittest!"c++"(E.b) == "assert(E::b == 1);"); 398 static assert(reflectGetUnittest!"c++"(E.c) is null); 399 400 struct S 401 { 402 @reflectUnittest!"c++"("alpha") 403 int a; 404 } 405 406 static assert(reflectGetUnittest!("c++", S.a) == "alpha"); 407 } 408 409 /++ 410 Returns: single UDA. 411 +/ 412 template getUDA(alias symbol, alias attribute) 413 { 414 private alias all = getUDAs!(symbol, attribute); 415 static if (all.length != 1) 416 static assert(0, "Exactly one " ~ attribute.stringof ~ " attribute is required, " ~ "got " ~ all.length.stringof); 417 else 418 { 419 static if (is(typeof(all[0]))) 420 enum getUDA = all[0]; 421 else 422 alias getUDA = all[0]; 423 } 424 } 425 426 /// ditto 427 template getUDA(T, string member, alias attribute) 428 { 429 private alias all = getUDAs!(T, member, attribute); 430 static if (all.length != 1) 431 static assert(0, "Exactly one " ~ attribute.stringof ~ " attribute is required, " ~ "got " ~ all.length.stringof); 432 else 433 { 434 static if (is(typeof(all[0]))) 435 enum getUDA = all[0]; 436 else 437 alias getUDA = all[0]; 438 } 439 } 440 441 /++ 442 Checks if T has a field member. 443 +/ 444 enum bool isOriginalMember(T, string member) = __traits(identifier, __traits(getMember, T, member)) == member; 445 446 /// 447 version(mir_core_test) unittest 448 { 449 struct D 450 { 451 int a; 452 alias b = a; 453 } 454 455 static assert(isOriginalMember!(D, "a")); 456 static assert(!isOriginalMember!(D, "b")); 457 } 458 459 /++ 460 Checks if T has a field member. 461 +/ 462 enum bool hasField(T, string member) = __traits(compiles, (ref T aggregate) { return __traits(getMember, aggregate, member).offsetof; }); 463 464 deprecated("use 'hasField' instead") alias isField = hasField; 465 466 /// 467 version(mir_core_test) unittest 468 { 469 struct D 470 { 471 int gi; 472 } 473 474 struct I 475 { 476 int f; 477 478 D base; 479 alias base this; 480 481 void gi(double ) @property {} 482 void gi(uint ) @property {} 483 } 484 485 struct S 486 { 487 int d; 488 489 I i; 490 alias i this; 491 492 int gm() @property {return 0;} 493 int gc() const @property {return 0;} 494 void gs(int) @property {} 495 } 496 497 static assert(!hasField!(S, "gi")); 498 static assert(!hasField!(S, "gs")); 499 static assert(!hasField!(S, "gc")); 500 static assert(!hasField!(S, "gm")); 501 static assert(!hasField!(S, "gi")); 502 static assert(hasField!(S, "d")); 503 static assert(hasField!(S, "f")); 504 static assert(hasField!(S, "i")); 505 } 506 507 /// with classes 508 version(mir_core_test) unittest 509 { 510 class I 511 { 512 int f; 513 514 void gi(double ) @property {} 515 void gi(uint ) @property {} 516 } 517 518 class S 519 { 520 int d; 521 522 I i; 523 alias i this; 524 525 int gm() @property {return 0;} 526 int gc() const @property {return 0;} 527 void gs(int) @property {} 528 } 529 530 static assert(!hasField!(S, "gi")); 531 static assert(!hasField!(S, "gs")); 532 static assert(!hasField!(S, "gc")); 533 static assert(!hasField!(S, "gm")); 534 static assert(hasField!(S, "d")); 535 static assert(hasField!(S, "f")); 536 static assert(hasField!(S, "i")); 537 } 538 539 /++ 540 Checks if member is property. 541 +/ 542 template isProperty(T, string member) 543 { 544 private __gshared T* aggregate; 545 546 static if (__traits(compiles, isSomeFunction!(__traits(getMember, *aggregate, member)))) 547 { 548 static if (isSomeFunction!(__traits(getMember, *aggregate, member))) 549 { 550 enum bool isProperty = isPropertyImpl!(__traits(getMember, *aggregate, member)); 551 } 552 else 553 { 554 enum bool isProperty = false; 555 } 556 } 557 else 558 enum bool isProperty = false; 559 } 560 561 /// 562 version(mir_core_test) unittest 563 { 564 struct D 565 { 566 int y; 567 568 void gf(double ) @property {} 569 void gf(uint ) @property {} 570 } 571 572 struct I 573 { 574 int f; 575 576 D base; 577 alias base this; 578 579 void gi(double ) @property {} 580 void gi(uint ) @property {} 581 } 582 583 struct S 584 { 585 int d; 586 587 I i; 588 alias i this; 589 590 int gm() @property {return 0;} 591 int gc() const @property {return 0;} 592 void gs(int) @property {} 593 } 594 595 static assert(isProperty!(S, "gf")); 596 static assert(isProperty!(S, "gi")); 597 static assert(isProperty!(S, "gs")); 598 static assert(isProperty!(S, "gc")); 599 static assert(isProperty!(S, "gm")); 600 static assert(!isProperty!(S, "d")); 601 static assert(!isProperty!(S, "f")); 602 static assert(!isProperty!(S, "y")); 603 } 604 605 version(mir_core_test) unittest 606 { 607 struct S 608 { 609 @reflectSerde enum s = "str"; 610 enum t = "str"; 611 } 612 static assert(hasUDA!(S, "s", reflectSerde)); 613 static assert(!hasUDA!(S, "t", reflectSerde)); 614 } 615 616 /++ 617 Returns: list of the setter properties. 618 619 Note: The implementation ignores templates. 620 +/ 621 template getSetters(T, string member) 622 { 623 static if (__traits(hasMember, T, member)) 624 alias getSetters = Filter!(hasSingleArgument, Filter!(isPropertyImpl, __traits(getOverloads, T, member, true))); 625 else 626 alias getSetters = AliasSeq!(); 627 } 628 629 /// 630 version(mir_core_test) unittest 631 { 632 struct I 633 { 634 int f; 635 636 void gi(double ) @property {} 637 void gi(uint ) @property {} 638 } 639 640 struct S 641 { 642 int d; 643 644 I i; 645 alias i this; 646 647 int gm() @property {return 0;} 648 int gc() const @property {return 0;} 649 void gs(int) @property {} 650 } 651 652 static assert(getSetters!(S, "gi").length == 2); 653 static assert(getSetters!(S, "gs").length == 1); 654 static assert(getSetters!(S, "gc").length == 0); 655 static assert(getSetters!(S, "gm").length == 0); 656 static assert(getSetters!(S, "d").length == 0); 657 static assert(getSetters!(S, "f").length == 0); 658 } 659 660 /++ 661 Returns: list of the serializable (public getters) members. 662 +/ 663 enum string[] SerializableMembers(T) = [Filter!(ApplyLeft!(Serializable, T), SerdeFieldsAndProperties!T)]; 664 665 /// 666 version(mir_core_test) unittest 667 { 668 struct D 669 { 670 int y; 671 672 int gf() @property {return 0;} 673 } 674 675 struct I 676 { 677 int f; 678 679 D base; 680 alias base this; 681 682 int gi() @property {return 0;} 683 } 684 685 struct S 686 { 687 int d; 688 689 package int p; 690 691 enum s = "str"; 692 @reflectSerde enum t = "str"; 693 694 int gm() @property {return 0;} 695 696 private int q; 697 698 I i; 699 alias i this; 700 701 int gc() const @property {return 0;} 702 void gs(int) @property {} 703 } 704 705 static assert(SerializableMembers!S == ["y", "gf", "f", "gi", "d", "t", "gm", "gc"]); 706 static assert(SerializableMembers!(const S) == ["y", "f", "d", "t", "gc"]); 707 } 708 709 /++ 710 Returns: list of the deserializable (public setters) members. 711 +/ 712 enum string[] DeserializableMembers(T) = [Filter!(ApplyLeft!(Deserializable, T), SerdeFieldsAndProperties!T)]; 713 714 /// 715 version(mir_core_test) unittest 716 { 717 struct I 718 { 719 int f; 720 void ga(int) @property {} 721 } 722 723 struct S 724 { 725 int d; 726 package int p; 727 728 int gm() @property {return 0;} 729 void gm(int) @property {} 730 731 private int q; 732 733 I i; 734 alias i this; 735 736 737 void gc(int, int) @property {} 738 void gc(int) @property {} 739 } 740 741 S s; 742 // s.gc(0); 743 744 static assert (DeserializableMembers!S == ["f", "ga", "d", "gm", "gc"]); 745 static assert (DeserializableMembers!(const S) == []); 746 } 747 748 // This trait defines what members should be serialized - 749 // public members that are either readable and writable or getter properties 750 private template Serializable(T, string member) 751 { 752 static if (!isPublic!(T, member)) 753 enum Serializable = false; 754 else 755 enum Serializable = isReadable!(T, member); // any readable is good 756 } 757 758 private enum bool hasSingleArgument(alias fun) = Parameters!fun.length == 1; 759 private enum bool hasZeroArguments(alias fun) = Parameters!fun.length == 0; 760 761 // This trait defines what members should be serialized - 762 // public members that are either readable and writable or setter properties 763 private template Deserializable(T, string member) 764 { 765 static if (!isPublic!(T, member)) 766 enum Deserializable = false; 767 else 768 static if (isReadableAndWritable!(T, member)) 769 enum Deserializable = true; 770 else 771 static if (getSetters!(T, member).length == 1) 772 enum Deserializable = is(typeof((ref T val){ __traits(getMember, val, member) = Parameters!(getSetters!(T, member)[0])[0].init; })); 773 else 774 enum Deserializable = false; 775 } 776 777 private enum SerdeFieldsAndProperties(T) = Reverse!(NoDuplicates!(Reverse!(SerdeFieldsAndPropertiesImpl!T))); 778 779 780 private static immutable exlMembers = [ 781 "opAssign", 782 "opCast", 783 "opCmp", 784 "opEquals", 785 "opPostMove", 786 "toHash", 787 "toString", 788 "trustedGet", 789 "deserializeFromAsdf", 790 "deserializeFromIon", 791 ]; 792 793 private auto filterMembers(string[] members) 794 { 795 string[] ret; 796 797 L: foreach(member; members) 798 { 799 if (member.length >= 2 && (member[0 .. 2] == "__" || member[$ - 2 .. $] == "__")) 800 continue; 801 foreach(exlMember; exlMembers) 802 if (exlMember == member) 803 continue L; 804 ret ~= member; 805 } 806 return ret; 807 }; 808 809 private template allMembers(T) 810 { 811 import std.meta: aliasSeqOf; 812 static if (isAggregateType!T) 813 alias allMembers = aliasSeqOf!(filterMembers([__traits(allMembers, T)])); 814 else 815 alias allMembers = AliasSeq!(); 816 } 817 818 private template SerdeFieldsAndPropertiesImpl(T) 819 { 820 alias isProperty = ApplyLeft!(.isProperty, T); 821 alias hasField = ApplyLeft!(.hasField, T); 822 alias isOriginalMember = ApplyLeft!(.isOriginalMember, T); 823 private __gshared T* aggregate; 824 template hasReflectSerde(string member) 825 { 826 static if (is(typeof(__traits(getMember, *aggregate, member)))) 827 enum hasReflectSerde = hasUDA!(T, member, reflectSerde); 828 else 829 enum hasReflectSerde = false; 830 } 831 alias isMember = templateAnd!(templateOr!(hasField, isProperty, hasReflectSerde), isOriginalMember); 832 static if (!is(immutable T == Tuple!Types, Types...) && __traits(getAliasThis, T).length) 833 { 834 alias A = typeof(__traits(getMember, aggregate, __traits(getAliasThis, T))); 835 static if (isAggregateType!T) 836 alias baseMembers = SerdeFieldsAndPropertiesImpl!A; 837 else 838 alias baseMembers = AliasSeq!(); 839 alias members = Erase!(__traits(getAliasThis, T)[0], __traits(allMembers, T)); 840 alias SerdeFieldsAndPropertiesImpl = AliasSeq!(baseMembers, Filter!(isMember, members)); 841 } 842 else 843 { 844 import mir.algebraic; 845 static if (isVariant!T) 846 alias members = staticMap!(allMembers, T.AllowedTypes); 847 else 848 alias members = allMembers!T; 849 alias SerdeFieldsAndPropertiesImpl = AliasSeq!(Filter!(isMember, members)); 850 } 851 } 852 853 // check if the member is readable 854 private template isReadable(T, string member) 855 { 856 private __gshared T* aggregate; 857 enum bool isReadable = __traits(compiles, { static fun(T)(auto ref T t) {} fun(__traits(getMember, *aggregate, member)); }); 858 } 859 860 // check if the member is readable/writeble? 861 private template isReadableAndWritable(T, string member) 862 { 863 private __gshared T* aggregate; 864 enum bool isReadableAndWritable = __traits(compiles, __traits(getMember, *aggregate, member) = __traits(getMember, *aggregate, member)); 865 } 866 867 package template isPublic(T, string member) 868 { 869 private __gshared T* aggregate; 870 static if (__traits(compiles, { auto _ = __traits(getProtection, __traits(getMember, *aggregate, member)); })) 871 enum bool isPublic = !__traits(getProtection, __traits(getMember, *aggregate, member)).privateOrPackage; 872 else 873 enum bool isPublic = false; 874 } 875 876 version(unittest) 877 { 878 final class ZipArchive 879 { 880 public: 881 static const ushort zip64ExtractVersion = 45; 882 } 883 } 884 885 version (mir_core_test) @nogc nothrow pure @safe unittest 886 { 887 static assert(!isPublic!(ZipArchive, "zip64ExtractVersion")); 888 } 889 890 // check if the member is property 891 private template isSetter(T, string member) 892 { 893 private __gshared T* aggregate; 894 static if (__traits(compiles, isSomeFunction!(__traits(getMember, *aggregate, member)))) 895 { 896 static if (isSomeFunction!(__traits(getMember, *aggregate, member))) 897 { 898 enum bool isSetter = getSetters!(T, member).length > 0;; 899 } 900 else 901 { 902 enum bool isSetter = false; 903 } 904 } 905 else 906 enum bool isSetter = false; 907 } 908 909 private template isGetter(T, string member) 910 { 911 private __gshared T* aggregate; 912 static if (__traits(compiles, isSomeFunction!(__traits(getMember, *aggregate, member)))) 913 { 914 static if (isSomeFunction!(__traits(getMember, *aggregate, member))) 915 { 916 enum bool isGetter = Filter!(hasZeroArguments, Filter!(isPropertyImpl, __traits(getOverloads, T, member, true))).length == 1; 917 } 918 else 919 { 920 enum bool isGetter = false; 921 } 922 } 923 else 924 enum bool isGetter = false; 925 } 926 927 private enum bool isPropertyImpl(alias member) = (functionAttributes!member & FunctionAttribute.property) != 0; 928 929 private bool privateOrPackage()(string protection) 930 { 931 return protection == "private" || protection == "package"; 932 }