1 module boilerplate.constructor; 2 3 import std.algorithm : canFind, map; 4 import std.meta : ApplyLeft, allSatisfy; 5 import std.range : array; 6 import std.traits : hasElaborateDestructor, isInstanceOf, isNested; 7 import std.typecons : Tuple; 8 9 version(unittest) 10 { 11 import unit_threaded.should; 12 } 13 14 /++ 15 GenerateThis is a mixin string that automatically generates a this() function, customizable with UDA. 16 +/ 17 public enum string GenerateThis = ` 18 import boilerplate.constructor: GenerateThisTemplate; 19 import std.string : replace; 20 mixin GenerateThisTemplate; 21 mixin(typeof(this).generateThisImpl()); 22 `; 23 24 /// 25 @("creates a constructor") 26 unittest 27 { 28 class Class 29 { 30 int field; 31 32 mixin(GenerateThis); 33 } 34 35 auto obj = new Class(5); 36 37 obj.field.shouldEqual(5); 38 } 39 40 /// 41 @("calls the super constructor if it exists") 42 unittest 43 { 44 class Class 45 { 46 int field; 47 48 mixin(GenerateThis); 49 } 50 51 class Child : Class 52 { 53 int field2; 54 55 mixin(GenerateThis); 56 } 57 58 auto obj = new Child(5, 8); 59 60 obj.field.shouldEqual(5); 61 obj.field2.shouldEqual(8); 62 } 63 64 /// 65 @("separates fields from methods") 66 unittest 67 { 68 class Class 69 { 70 int field; 71 72 void method() { } 73 74 mixin(GenerateThis); 75 } 76 77 auto obj = new Class(5); 78 79 obj.field.shouldEqual(5); 80 } 81 82 /// 83 @("dups arrays") 84 unittest 85 { 86 class Class 87 { 88 int[] array; 89 90 mixin(GenerateThis); 91 } 92 93 auto array = [2, 3, 4]; 94 auto obj = new Class(array); 95 96 array[0] = 1; 97 obj.array[0].shouldEqual(2); 98 } 99 100 /// 101 @("dups arrays hidden behind Nullable") 102 unittest 103 { 104 import std.typecons : Nullable, nullable; 105 106 class Class 107 { 108 Nullable!(int[]) array; 109 110 mixin(GenerateThis); 111 } 112 113 auto array = [2, 3, 4]; 114 auto obj = new Class(array.nullable); 115 116 array[0] = 1; 117 obj.array.get[0].shouldEqual(2); 118 119 obj = new Class(Nullable!(int[]).init); 120 obj.array.isNull.shouldBeTrue; 121 } 122 123 /// 124 @("uses default value for default constructor parameter") 125 unittest 126 { 127 class Class 128 { 129 @(This.Default!5) 130 int value = 5; 131 132 mixin(GenerateThis); 133 } 134 135 auto obj1 = new Class(); 136 137 obj1.value.shouldEqual(5); 138 139 auto obj2 = new Class(6); 140 141 obj2.value.shouldEqual(6); 142 } 143 144 /// 145 @("creates no constructor for an empty struct") 146 unittest 147 { 148 struct Struct 149 { 150 mixin(GenerateThis); 151 } 152 153 auto strct = Struct(); 154 } 155 156 /// 157 @("properly generates new default values on each call") 158 unittest 159 { 160 import std.conv : to; 161 162 class Class 163 { 164 @(This.Default!(() => new Object)) 165 Object obj; 166 167 mixin(GenerateThis); 168 } 169 170 auto obj1 = new Class(); 171 auto obj2 = new Class(); 172 173 (cast(void*) obj1.obj).shouldNotEqual(cast(void*) obj2.obj); 174 } 175 176 /// 177 @("establishes the parent-child parameter order: parent explicit, child explicit, child implicit, parent implicit.") 178 unittest 179 { 180 class Parent 181 { 182 int field1; 183 184 @(This.Default!2) 185 int field2 = 2; 186 187 mixin(GenerateThis); 188 } 189 190 class Child : Parent 191 { 192 int field3; 193 194 @(This.Default!4) 195 int field4 = 4; 196 197 mixin(GenerateThis); 198 } 199 200 auto obj = new Child(1, 2, 3, 4); 201 202 obj.field1.shouldEqual(1); 203 obj.field3.shouldEqual(2); 204 obj.field4.shouldEqual(3); 205 obj.field2.shouldEqual(4); 206 } 207 208 /// 209 @("disregards static fields") 210 unittest 211 { 212 class Class 213 { 214 static int field1; 215 int field2; 216 217 mixin(GenerateThis); 218 } 219 220 auto obj = new Class(5); 221 222 obj.field1.shouldEqual(0); 223 obj.field2.shouldEqual(5); 224 } 225 226 /// 227 @("can initialize with immutable arrays") 228 unittest 229 { 230 class Class 231 { 232 immutable(Object)[] array; 233 234 mixin(GenerateThis); 235 } 236 } 237 238 /// 239 @("can define scope for constructor") 240 unittest 241 { 242 @(This.Private) 243 class PrivateClass 244 { 245 mixin(GenerateThis); 246 } 247 248 @(This.Protected) 249 class ProtectedClass 250 { 251 mixin(GenerateThis); 252 } 253 254 @(This.Package) 255 class PackageClass 256 { 257 mixin(GenerateThis); 258 } 259 260 @(This.Package("boilerplate")) 261 class SubPackageClass 262 { 263 mixin(GenerateThis); 264 } 265 266 class PublicClass 267 { 268 mixin(GenerateThis); 269 } 270 271 static assert(__traits(getProtection, PrivateClass.__ctor) == "private"); 272 static assert(__traits(getProtection, ProtectedClass.__ctor) == "protected"); 273 static assert(__traits(getProtection, PackageClass.__ctor) == "package"); 274 // getProtection does not return the package name of a package() attribute 275 // static assert(__traits(getProtection, SubPackageClass.__ctor) == `package(boilerplate)`); 276 static assert(__traits(getProtection, PublicClass.__ctor) == "public"); 277 } 278 279 /// 280 @("will assign the same scope to Builder") 281 unittest 282 { 283 @(This.Private) 284 class PrivateClass 285 { 286 mixin(GenerateThis); 287 } 288 289 @(This.Protected) 290 class ProtectedClass 291 { 292 mixin(GenerateThis); 293 } 294 295 @(This.Package) 296 class PackageClass 297 { 298 mixin(GenerateThis); 299 } 300 301 @(This.Package("boilerplate")) 302 class SubPackageClass 303 { 304 mixin(GenerateThis); 305 } 306 307 class PublicClass 308 { 309 mixin(GenerateThis); 310 } 311 312 static assert(__traits(getProtection, PrivateClass.Builder) == "private"); 313 static assert(__traits(getProtection, ProtectedClass.Builder) == "protected"); 314 static assert(__traits(getProtection, PackageClass.Builder) == "package"); 315 static assert(__traits(getProtection, PublicClass.Builder) == "public"); 316 } 317 318 /// 319 @("can use default tag with new") 320 unittest 321 { 322 class Class 323 { 324 @(This.Default!(() => new Object)) 325 Object foo; 326 327 mixin(GenerateThis); 328 } 329 330 ((new Class).foo !is null).shouldBeTrue; 331 } 332 333 /// 334 @("empty default tag means T()") 335 unittest 336 { 337 class Class 338 { 339 @(This.Default) 340 string s; 341 342 @(This.Default) 343 int i; 344 345 mixin(GenerateThis); 346 } 347 348 (new Class()).i.shouldEqual(0); 349 (new Class()).s.shouldEqual(null); 350 } 351 352 /// 353 @("can exclude fields from constructor") 354 unittest 355 { 356 class Class 357 { 358 @(This.Exclude) 359 int i = 5; 360 361 mixin(GenerateThis); 362 } 363 364 (new Class).i.shouldEqual(5); 365 } 366 367 /// 368 @("marks duppy parameters as const when this does not prevent dupping") 369 unittest 370 { 371 372 struct Struct 373 { 374 } 375 376 class Class 377 { 378 Struct[] values_; 379 380 mixin(GenerateThis); 381 } 382 383 const Struct[] constValues; 384 auto obj = new Class(constValues); 385 } 386 387 /// 388 @("does not include property functions in constructor list") 389 unittest 390 { 391 class Class 392 { 393 int a; 394 395 @property int foo() const 396 { 397 return 0; 398 } 399 400 mixin(GenerateThis); 401 } 402 403 static assert(__traits(compiles, new Class(0))); 404 static assert(!__traits(compiles, new Class(0, 0))); 405 } 406 407 @("declares @nogc on non-dupping constructors") 408 @nogc unittest 409 { 410 struct Struct 411 { 412 int a; 413 414 mixin(GenerateThis); 415 } 416 417 auto str = Struct(5); 418 } 419 420 /// 421 @("can initialize fields using init value") 422 unittest 423 { 424 class Class 425 { 426 @(This.Init!5) 427 int field1; 428 429 @(This.Init!(() => 8)) 430 int field2; 431 432 mixin(GenerateThis); 433 } 434 435 auto obj = new Class; 436 437 obj.field1.shouldEqual(5); 438 obj.field2.shouldEqual(8); 439 } 440 441 /// 442 @("can initialize fields using init value, with lambda that accesses previous value") 443 unittest 444 { 445 class Class 446 { 447 int field1; 448 449 @(This.Init!(self => self.field1 + 5)) 450 int field2; 451 452 mixin(GenerateThis); 453 } 454 455 auto obj = new Class(5); 456 457 obj.field1.shouldEqual(5); 458 obj.field2.shouldEqual(10); 459 } 460 461 /// 462 @("can initialize fields with allocated types") 463 unittest 464 { 465 class Class1 466 { 467 @(This.Init!(self => new Object)) 468 Object object; 469 470 mixin(GenerateThis); 471 } 472 473 class Class2 474 { 475 @(This.Init!(() => new Object)) 476 Object object; 477 478 mixin(GenerateThis); 479 } 480 481 class Class3 : Class2 482 { 483 mixin(GenerateThis); 484 } 485 } 486 487 /// 488 @("generates Builder class that gathers constructor parameters, then calls constructor with them") 489 unittest 490 { 491 static class Class 492 { 493 int field1; 494 int field2; 495 int field3; 496 497 mixin(GenerateThis); 498 } 499 500 auto obj = { 501 with (Class.Builder()) 502 { 503 field1 = 1; 504 field2 = 2; 505 field3 = 3; 506 return value; 507 } 508 }(); 509 510 with (obj) 511 { 512 field1.shouldEqual(1); 513 field2.shouldEqual(2); 514 field3.shouldEqual(3); 515 } 516 } 517 518 /// 519 @("builder field order doesn't matter") 520 unittest 521 { 522 static class Class 523 { 524 int field1; 525 int field2; 526 int field3; 527 528 mixin(GenerateThis); 529 } 530 531 auto obj = { 532 with (Class.Builder()) 533 { 534 field3 = 1; 535 field1 = 2; 536 field2 = 3; 537 return value; 538 } 539 }(); 540 541 with (obj) 542 { 543 field1.shouldEqual(2); 544 field2.shouldEqual(3); 545 field3.shouldEqual(1); 546 } 547 } 548 549 /// 550 @("default fields can be left out when assigning builder") 551 unittest 552 { 553 static class Class 554 { 555 int field1; 556 @(This.Default!5) 557 int field2; 558 int field3; 559 560 mixin(GenerateThis); 561 } 562 563 // constructor is this(field1, field3, field2 = 5) 564 auto obj = { 565 with (Class.Builder()) 566 { 567 field1 = 1; 568 field3 = 3; 569 return value; 570 } 571 }(); 572 573 with (obj) 574 { 575 field1.shouldEqual(1); 576 field2.shouldEqual(5); 577 field3.shouldEqual(3); 578 } 579 } 580 581 /// 582 @("supports Builder in structs") 583 unittest 584 { 585 struct Struct 586 { 587 int field1; 588 int field2; 589 int field3; 590 591 mixin(GenerateThis); 592 } 593 594 auto value = { 595 with (Struct.Builder()) 596 { 597 field1 = 1; 598 field3 = 3; 599 field2 = 5; 600 return value; 601 } 602 }(); 603 604 static assert(is(typeof(value) == Struct)); 605 606 with (value) 607 { 608 field1.shouldEqual(1); 609 field2.shouldEqual(5); 610 field3.shouldEqual(3); 611 } 612 } 613 614 /// 615 @("builder strips trailing underlines") 616 unittest 617 { 618 struct Struct 619 { 620 private int a_; 621 622 mixin(GenerateThis); 623 } 624 625 auto builder = Struct.Builder(); 626 627 builder.a = 1; 628 629 auto value = builder.value; 630 631 value.shouldEqual(Struct(1)); 632 } 633 634 /// 635 @("builder supports nested initialization") 636 unittest 637 { 638 struct Struct1 639 { 640 int a; 641 int b; 642 643 mixin(GenerateThis); 644 } 645 646 struct Struct2 647 { 648 int c; 649 Struct1 struct1; 650 int d; 651 652 mixin(GenerateThis); 653 } 654 655 auto builder = Struct2.Builder(); 656 657 builder.struct1.a = 1; 658 builder.struct1.b = 2; 659 builder.c = 3; 660 builder.d = 4; 661 662 auto value = builder.value; 663 664 static assert(is(typeof(value) == Struct2)); 665 666 with (value) 667 { 668 struct1.a.shouldEqual(1); 669 struct1.b.shouldEqual(2); 670 c.shouldEqual(3); 671 d.shouldEqual(4); 672 } 673 } 674 675 /// 676 @("builder supports defaults for nested values") 677 unittest 678 { 679 struct Struct1 680 { 681 int a; 682 int b; 683 684 mixin(GenerateThis); 685 } 686 687 struct Struct2 688 { 689 int c; 690 @(This.Default!(Struct1(3, 4))) 691 Struct1 struct1; 692 int d; 693 694 mixin(GenerateThis); 695 } 696 697 auto builder = Struct2.Builder(); 698 699 builder.c = 1; 700 builder.d = 2; 701 702 builder.value.shouldEqual(Struct2(1, 2, Struct1(3, 4))); 703 } 704 705 /// 706 @("builder supports direct value assignment for nested values") 707 unittest 708 { 709 struct Struct1 710 { 711 int a; 712 int b; 713 714 mixin(GenerateThis); 715 } 716 717 struct Struct2 718 { 719 int c; 720 Struct1 struct1; 721 int d; 722 723 mixin(GenerateThis); 724 } 725 726 auto builder = Struct2.Builder(); 727 728 builder.struct1 = Struct1(2, 3); 729 builder.c = 1; 730 builder.d = 4; 731 732 builder.value.shouldEqual(Struct2(1, Struct1(2, 3), 4)); 733 } 734 735 /// 736 @("builder supports const args") 737 unittest 738 { 739 struct Struct 740 { 741 const int a; 742 743 mixin(GenerateThis); 744 } 745 746 with (Struct.Builder()) 747 { 748 a = 5; 749 750 value.shouldEqual(Struct(5)); 751 } 752 } 753 754 /// 755 @("builder supports fields with destructor") 756 unittest 757 { 758 static struct Struct1 759 { 760 ~this() pure @safe @nogc nothrow { } 761 } 762 763 struct Struct2 764 { 765 Struct1 struct1; 766 767 mixin(GenerateThis); 768 } 769 770 with (Struct2.Builder()) 771 { 772 struct1 = Struct1(); 773 774 value.shouldEqual(Struct2(Struct1())); 775 } 776 } 777 778 /// 779 @("builder supports direct assignment to Nullables") 780 unittest 781 { 782 import std.typecons : Nullable, nullable; 783 784 struct Struct 785 { 786 const Nullable!int a; 787 788 mixin(GenerateThis); 789 } 790 791 with (Struct.Builder()) 792 { 793 a = 5; 794 795 value.shouldEqual(Struct(5.nullable)); 796 } 797 } 798 799 /// 800 @("builder supports reconstruction from value") 801 unittest 802 { 803 import std.typecons : Nullable, nullable; 804 805 struct Struct 806 { 807 private int a_; 808 809 int[] b; 810 811 mixin(GenerateThis); 812 } 813 814 const originalValue = Struct(2, [3]); 815 816 with (originalValue.BuilderFrom()) 817 { 818 a = 5; 819 820 value.shouldEqual(Struct(5, [3])); 821 } 822 } 823 824 import std.string : format; 825 826 enum GetSuperTypeAsString_(string member) = format!`typeof(super).ConstructorInfo.FieldInfo.%s.Type`(member); 827 828 enum GetMemberTypeAsString_(string member) = format!`typeof(this.%s)`(member); 829 830 enum SuperDefault_(string member) = format!`typeof(super).ConstructorInfo.FieldInfo.%s.fieldDefault`(member); 831 832 enum MemberDefault_(string member) = 833 format!`getUDADefaultOrNothing!(typeof(this.%s), __traits(getAttributes, this.%s))`(member, member); 834 835 enum SuperUseDefault_(string member) 836 = format!(`typeof(super).ConstructorInfo.FieldInfo.%s.useDefault`)(member); 837 838 enum MemberUseDefault_(string member) 839 = format!(`udaIndex!(This.Default, __traits(getAttributes, this.%s)) != -1`)(member); 840 841 enum SuperAttributes_(string member) 842 = format!(`typeof(super).ConstructorInfo.FieldInfo.%s.attributes`)(member); 843 844 enum MemberAttributes_(string member) 845 = format!(`__traits(getAttributes, this.%s)`)(member); 846 847 mixin template GenerateThisTemplate() 848 { 849 private static generateThisImpl() 850 { 851 if (!__ctfe) 852 { 853 return null; 854 } 855 856 import boilerplate.constructor : 857 GetMemberTypeAsString_, GetSuperTypeAsString_, 858 MemberDefault_, SuperDefault_, 859 MemberUseDefault_, SuperUseDefault_, 860 MemberAttributes_, SuperAttributes_, 861 This; 862 import boilerplate.util : GenNormalMemberTuple, bucketSort, needToDup, 863 reorder, udaIndex, removeTrailingUnderline; 864 import std.algorithm : all, canFind, filter, map; 865 import std.meta : Alias, aliasSeqOf, staticMap; 866 import std.range : array, drop, iota, zip; 867 import std.string : endsWith, format, join; 868 import std.typecons : Nullable; 869 870 mixin GenNormalMemberTuple; 871 872 string result = null; 873 874 string visibility = "public"; 875 876 foreach (uda; __traits(getAttributes, typeof(this))) 877 { 878 static if (is(typeof(uda) == ThisEnum)) 879 { 880 static if (uda == This.Protected) 881 { 882 visibility = "protected"; 883 } 884 static if (uda == This.Private) 885 { 886 visibility = "private"; 887 } 888 } 889 else static if (is(uda == This.Package)) 890 { 891 visibility = "package"; 892 } 893 else static if (is(typeof(uda) == This.Package)) 894 { 895 visibility = "package(" ~ uda.packageMask ~ ")"; 896 } 897 } 898 899 string[] constructorAttributes = ["pure", "nothrow", "@safe", "@nogc"]; 900 901 static if (is(typeof(typeof(super).ConstructorInfo))) 902 { 903 enum argsPassedToSuper = typeof(super).ConstructorInfo.fields.length; 904 enum members = typeof(super).ConstructorInfo.fields ~ [NormalMemberTuple]; 905 enum string[] CombinedArray(alias SuperPred, alias MemberPred) = ([ 906 staticMap!(SuperPred, aliasSeqOf!(typeof(super).ConstructorInfo.fields)), 907 staticMap!(MemberPred, NormalMemberTuple) 908 ]); 909 constructorAttributes = typeof(super).GeneratedConstructorAttributes_; 910 } 911 else 912 { 913 enum argsPassedToSuper = 0; 914 static if (NormalMemberTuple.length > 0) 915 { 916 enum members = [NormalMemberTuple]; 917 enum string[] CombinedArray(alias SuperPred, alias MemberPred) = ([ 918 staticMap!(MemberPred, NormalMemberTuple) 919 ]); 920 } 921 else 922 { 923 enum string[] members = null; 924 enum string[] CombinedArray(alias SuperPred, alias MemberPred) = null; 925 } 926 } 927 928 enum string[] useDefaults = CombinedArray!(SuperUseDefault_, MemberUseDefault_); 929 enum string[] memberTypes = CombinedArray!(GetSuperTypeAsString_, GetMemberTypeAsString_); 930 enum string[] defaults = CombinedArray!(SuperDefault_, MemberDefault_); 931 enum string[] attributes = CombinedArray!(SuperAttributes_, MemberAttributes_); 932 933 string[] fields; 934 string[] args; 935 string[] argexprs; 936 string[] defaultAssignments; 937 bool[] fieldUseDefault; 938 string[] fieldDefault; 939 string[] fieldAttributes; 940 string[] types; 941 string[] directInitFields; 942 int[] directInitIndex; 943 bool[] directInitUseSelf; 944 945 foreach (i; aliasSeqOf!(members.length.iota)) 946 { 947 enum member = members[i]; 948 949 mixin(`alias Type = ` ~ memberTypes[i] ~ `;`); 950 mixin(`enum bool useDefault = ` ~ useDefaults[i] ~ `;`); 951 952 bool includeMember = false; 953 954 enum isNullable = is(Type: Nullable!Arg, Arg); 955 956 static if (!isNullable) 957 { 958 bool dupExpr = needToDup!Type; 959 bool passExprAsConst = dupExpr && __traits(compiles, const(Type).init.dup); 960 } 961 else 962 { 963 // unpack nullable for dup 964 bool dupExpr = needToDup!(typeof(Type.init.get)); 965 bool passExprAsConst = dupExpr && __traits(compiles, Type(const(Type).init.get.dup)); 966 } 967 968 bool forSuper = false; 969 970 static if (i < argsPassedToSuper) 971 { 972 includeMember = true; 973 forSuper = true; 974 } 975 else 976 { 977 mixin("alias symbol = typeof(this)." ~ member ~ ";"); 978 979 static assert (is(typeof(symbol)) && !__traits(isTemplate, symbol)); /* must have a resolvable type */ 980 981 import boilerplate.util: isStatic; 982 983 includeMember = !mixin(isStatic(member)); 984 985 static if (udaIndex!(This.Init, __traits(getAttributes, symbol)) != -1) 986 { 987 enum udaFieldIndex = udaIndex!(This.Init, __traits(getAttributes, symbol)); 988 alias initArg = Alias!(__traits(getAttributes, symbol)[udaFieldIndex].value); 989 enum lambdaWithSelf = __traits(compiles, initArg(typeof(this).init)); 990 enum nakedLambda = __traits(compiles, initArg()); 991 992 directInitFields ~= member; 993 directInitIndex ~= udaFieldIndex; 994 directInitUseSelf ~= __traits(compiles, 995 __traits(getAttributes, symbol)[udaFieldIndex].value(typeof(this).init)); 996 includeMember = false; 997 998 static if (lambdaWithSelf) 999 { 1000 static if (__traits(compiles, initArg!(typeof(this)))) 1001 { 1002 enum lambdaAttributes = [__traits(getFunctionAttributes, initArg!(typeof(this)))]; 1003 } 1004 else 1005 { 1006 enum lambdaAttributes = [__traits(getFunctionAttributes, initArg)]; 1007 } 1008 1009 constructorAttributes = constructorAttributes.filter!(a => lambdaAttributes.canFind(a)).array; 1010 } 1011 else static if (nakedLambda) 1012 { 1013 enum lambdaAttributes = [__traits(getFunctionAttributes, initArg)]; 1014 1015 constructorAttributes = constructorAttributes.filter!(a => lambdaAttributes.canFind(a)).array; 1016 } 1017 } 1018 1019 static if (udaIndex!(This.Exclude, __traits(getAttributes, symbol)) != -1) 1020 { 1021 includeMember = false; 1022 } 1023 } 1024 1025 if (!includeMember) continue; 1026 1027 enum paramName = member.removeTrailingUnderline; 1028 1029 string argexpr = paramName; 1030 1031 if (dupExpr) 1032 { 1033 constructorAttributes = constructorAttributes.filter!(a => a != "@nogc").array; 1034 1035 static if (isNullable) 1036 { 1037 argexpr = format!`%s.isNull ? %s.init : %s(%s.get.dup)` 1038 (argexpr, memberTypes[i], memberTypes[i], argexpr); 1039 } 1040 else 1041 { 1042 argexpr = format!`%s.dup`(argexpr); 1043 } 1044 } 1045 1046 fields ~= member; 1047 args ~= paramName; 1048 argexprs ~= argexpr; 1049 fieldUseDefault ~= useDefault; 1050 fieldDefault ~= defaults[i]; 1051 fieldAttributes ~= attributes[i]; 1052 defaultAssignments ~= useDefault ? (` = ` ~ defaults[i]) : ``; 1053 types ~= passExprAsConst ? (`const ` ~ memberTypes[i]) : memberTypes[i]; 1054 } 1055 1056 size_t establishParameterRank(size_t i) 1057 { 1058 // parent explicit, our explicit, our implicit, parent implicit 1059 const fieldOfParent = i < argsPassedToSuper; 1060 return fieldUseDefault[i] * 2 + (fieldUseDefault[i] == fieldOfParent); 1061 } 1062 1063 auto constructorFieldOrder = fields.length.iota.array.bucketSort(&establishParameterRank); 1064 1065 assert(fields.length == types.length); 1066 assert(fields.length == fieldUseDefault.length); 1067 assert(fields.length == fieldDefault.length); 1068 1069 result ~= format!` 1070 public static alias ConstructorInfo = 1071 saveConstructorInfo!(%s, %-(%s, %));` 1072 ( 1073 fields.reorder(constructorFieldOrder), 1074 zip( 1075 types.reorder(constructorFieldOrder), 1076 fieldUseDefault.reorder(constructorFieldOrder), 1077 fieldDefault.reorder(constructorFieldOrder), 1078 fieldAttributes.reorder(constructorFieldOrder), 1079 ) 1080 .map!(args => format!`ConstructorField!(%s, %s, %s, %s)`(args[0], args[1], args[2], args[3])) 1081 .array 1082 ); 1083 1084 if (!(is(typeof(this) == struct) && fieldUseDefault.all)) // don't emit this() for structs 1085 { 1086 result ~= visibility ~ ` this(` 1087 ~ constructorFieldOrder 1088 .map!(i => format!`%s %s%s`(types[i], args[i], defaultAssignments[i])) 1089 .join(`, `) 1090 ~ format!`) %-(%s %)`(constructorAttributes); 1091 1092 result ~= `{`; 1093 1094 static if (is(typeof(typeof(super).ConstructorInfo))) 1095 { 1096 result ~= `super(` ~ args[0 .. argsPassedToSuper].join(", ") ~ `);`; 1097 } 1098 1099 result ~= fields.length.iota.drop(argsPassedToSuper) 1100 .map!(i => format!`this.%s = %s;`(fields[i], argexprs[i])) 1101 .join; 1102 1103 foreach (i, field; directInitFields) 1104 { 1105 if (directInitUseSelf[i]) 1106 { 1107 result ~= format!`this.%s = __traits(getAttributes, this.%s)[%s].value(this);` 1108 (field, field, directInitIndex[i]); 1109 } 1110 else 1111 { 1112 result ~= format!`this.%s = __traits(getAttributes, this.%s)[%s].value;` 1113 (field, field, directInitIndex[i]); 1114 } 1115 } 1116 1117 result ~= `}`; 1118 1119 result ~= `protected static enum string[] GeneratedConstructorAttributes_ = [` 1120 ~ constructorAttributes.map!(a => `"` ~ a ~ `"`).join(`, `) 1121 ~ `];`; 1122 } 1123 1124 result ~= visibility ~ ` static struct BuilderType(alias T = typeof(this)) 1125 { 1126 import boilerplate.builder : BuilderImpl; 1127 1128 mixin BuilderImpl!T; 1129 }`; 1130 1131 result ~= visibility ~ ` static auto Builder()() 1132 { 1133 return BuilderType!()(); 1134 }`; 1135 1136 result ~= visibility ~ ` auto BuilderFrom()() const 1137 { 1138 import boilerplate.util : removeTrailingUnderline; 1139 1140 auto builder = BuilderType!()(); 1141 1142 static foreach (field; ConstructorInfo.fields) 1143 { 1144 mixin("builder." ~ field.removeTrailingUnderline ~ " = this." ~ field ~ ";"); 1145 } 1146 return builder; 1147 }`; 1148 1149 return result; 1150 } 1151 } 1152 1153 public template ConstructorField(Type_, bool useDefault_, alias fieldDefault_, attributes_...) 1154 { 1155 public alias Type = Type_; 1156 public enum useDefault = useDefault_; 1157 public alias fieldDefault = fieldDefault_; 1158 public alias attributes = attributes_; 1159 } 1160 1161 public template saveConstructorInfo(string[] fields_, Fields...) 1162 if (fields_.length == Fields.length 1163 && allSatisfy!(ApplyLeft!(isInstanceOf, ConstructorField), Fields)) 1164 { 1165 import std.format : format; 1166 1167 public enum fields = fields_; 1168 1169 private template FieldInfo_() { 1170 static foreach (i, field; fields) 1171 { 1172 mixin(format!q{public alias %s = Fields[%s];}(field, i)); 1173 } 1174 } 1175 1176 public alias FieldInfo = FieldInfo_!(); 1177 } 1178 1179 enum ThisEnum 1180 { 1181 Private, 1182 Protected, 1183 Exclude 1184 } 1185 1186 struct This 1187 { 1188 enum Private = ThisEnum.Private; 1189 enum Protected = ThisEnum.Protected; 1190 struct Package 1191 { 1192 string packageMask = null; 1193 } 1194 enum Exclude = ThisEnum.Exclude; 1195 1196 // construct with value 1197 static struct Init(alias Alias) 1198 { 1199 static if (__traits(compiles, Alias())) 1200 { 1201 @property static auto value() { return Alias(); } 1202 } 1203 else 1204 { 1205 alias value = Alias; 1206 } 1207 } 1208 1209 static struct Default(alias Alias) 1210 { 1211 static if (__traits(compiles, Alias())) 1212 { 1213 @property static auto value() { return Alias(); } 1214 } 1215 else 1216 { 1217 alias value = Alias; 1218 } 1219 } 1220 } 1221 1222 public template getUDADefaultOrNothing(T, attributes...) 1223 { 1224 import boilerplate.util : udaIndex; 1225 1226 template EnumTest() 1227 { 1228 enum EnumTest = attributes[udaIndex!(This.Default, attributes)].value; 1229 } 1230 1231 static if (udaIndex!(This.Default, attributes) == -1) 1232 { 1233 enum getUDADefaultOrNothing = 0; 1234 } 1235 // @(This.Default) 1236 else static if (__traits(isSame, attributes[udaIndex!(This.Default, attributes)], This.Default)) 1237 { 1238 enum getUDADefaultOrNothing = T.init; 1239 } 1240 else static if (__traits(compiles, EnumTest!())) 1241 { 1242 enum getUDADefaultOrNothing = attributes[udaIndex!(This.Default, attributes)].value; 1243 } 1244 else 1245 { 1246 @property static auto getUDADefaultOrNothing() 1247 { 1248 return attributes[udaIndex!(This.Default, attributes)].value; 1249 } 1250 } 1251 }