1 /** 2 3 License: 4 Boost Software License - Version 1.0 - August 17th, 2003 5 6 Permission is hereby granted, free of charge, to any person or organization 7 obtaining a copy of the software and accompanying documentation covered by 8 this license (the "Software") to use, reproduce, display, distribute, 9 execute, and transmit the Software, and to prepare derivative works of the 10 Software, and to permit third-parties to whom the Software is furnished to 11 do so, all subject to the following: 12 13 The copyright notices in the Software and this entire statement, including 14 the above license grant, this restriction and the following disclaimer, 15 must be included in all copies of the Software, in whole or in part, and 16 all derivative works of the Software, unless such copies or derivative 17 works are solely in the form of machine-executable object code generated by 18 a source language processor. 19 20 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 23 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 24 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 25 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 26 DEALINGS IN THE SOFTWARE. 27 28 Authors: 29 Alexandru Ermicioi 30 **/ 31 module aermicioi.aedi.factory.generic_factory; 32 33 34 import aermicioi.aedi.exception; 35 import aermicioi.aedi.factory.factory; 36 import aermicioi.aedi.factory.reference; 37 import aermicioi.aedi.storage.decorator; 38 import aermicioi.aedi.storage.locator; 39 import aermicioi.aedi.storage.locator_aware; 40 import aermicioi.aedi.storage.wrapper; 41 import aermicioi.util.traits; 42 43 import std.conv : to; 44 import std.meta; 45 import std.traits; 46 import std.typecons; 47 48 /** 49 A property configurer, has the purpose to modify data of type T according to some logic encapsulated in it. 50 51 **/ 52 interface PropertyConfigurer(T) { 53 54 public { 55 56 /** 57 Accepts a reference to an object that is to be configured by the configurer. 58 59 Params: 60 object = An object of type T, that will be configured 61 **/ 62 void configure(ref T object); 63 } 64 } 65 66 /** 67 An instance factory, instantiates data of type T. 68 **/ 69 interface InstanceFactory(T) { 70 71 public { 72 73 /** 74 Create a new instance of object of type T. 75 76 Returns: 77 T instantiated component 78 **/ 79 T factory(); 80 } 81 } 82 83 /** 84 Interface for objects that are aware of an instance factory 85 and can use it to instantiate a component. 86 **/ 87 interface InstanceFactoryAware(T) { 88 public { 89 90 @property { 91 /** 92 Sets the constructor of new object. 93 94 Params: 95 factory = a factory of objects of type T. 96 97 Returns: 98 The InstanceFactoryAware 99 **/ 100 InstanceFactoryAware!T setInstanceFactory(InstanceFactory!T factory); 101 } 102 } 103 } 104 105 /** 106 Interface for objects that are aware of a set of property configurers 107 and can use them to configure some component. 108 **/ 109 interface PropertyConfigurersAware(T) { 110 public { 111 /** 112 Adds an configurer to the PropertyConfigurersAware. 113 114 Params: 115 configurer = a configurer that will be invoked after factory of an object. 116 117 Returns: 118 The PropertyConfigurersAware instance 119 **/ 120 PropertyConfigurersAware!T addPropertyConfigurer(PropertyConfigurer!T configurer); 121 } 122 } 123 /** 124 A generic factory, is a factory that instantiates data of type T using InstanceFactory and a list of PropertyConfigurers. 125 126 A generic factory, is a factory that instantiates data of type T using InstanceFactory and a list of PropertyConfigurers. 127 It can optionally provide a Locator!() object to InstanceFactory and PropertyConfigurer to be used as a source of data. 128 **/ 129 interface GenericFactory(T) : Factory!T, InstanceFactoryAware!T, PropertyConfigurersAware!T { 130 131 public { 132 133 @property { 134 135 alias locator = Factory!T.locator; 136 137 /** 138 Get the GenericFactory locator. 139 140 Returns: 141 Locator!() the locator that should be used by underlying constructor or property configurer. 142 **/ 143 Locator!() locator(); 144 } 145 } 146 } 147 148 /** 149 A concrete implementation of GenericFactory interface. 150 **/ 151 class GenericFactoryImpl(T) : GenericFactory!T, LocatorAware!() { 152 153 private { 154 Locator!() locator_; 155 156 InstanceFactory!T factory_; 157 158 PropertyConfigurer!T[] configurers; 159 } 160 161 public { 162 163 /** 164 Constructor for GenericFactoryImpl 165 166 Params: 167 locator = the locator used by constructor to fetch dependencies of the created object 168 **/ 169 this(Locator!() locator) { 170 this.locator = locator; 171 172 static if (hasDefaultCtor!T) { 173 174 this.setInstanceFactory( 175 new DefaultConstructorBasedFactory!T 176 ); 177 } 178 } 179 180 /** 181 Create a new instance of object of type T using provided instance factory and property configurers. 182 183 Returns: 184 T instantiated component 185 **/ 186 T factory() { 187 T instance; 188 189 if (this.factory_ !is null) { 190 instance = this.factory_.factory; 191 } else { 192 193 throw new AediException("Failed to construct object due to no constructor"); 194 } 195 196 foreach (key, configurer; this.configurers) { 197 configurer.configure(instance); 198 } 199 200 return instance; 201 } 202 203 @property { 204 205 /** 206 Sets the constructor of new object. 207 208 Params: 209 factory = a factory of objects of type T. 210 211 Returns: 212 The InstanceFactoryAware 213 **/ 214 GenericFactory!T setInstanceFactory(InstanceFactory!T factory) { 215 this.factory_ = factory; 216 217 return this; 218 } 219 220 /** 221 Set locator 222 223 Params: 224 locator = the locator used to fetch created's object dependencies 225 Returns: 226 typeof(this) 227 **/ 228 GenericFactoryImpl!T locator(Locator!() locator) { 229 this.locator_ = locator; 230 return this; 231 } 232 233 /** 234 Get locator 235 236 Returns: 237 Locator!() 238 **/ 239 Locator!() locator() { 240 return this.locator_; 241 } 242 243 /** 244 Get the type info of object that is created. 245 246 Returns: 247 TypeInfo object of created object. 248 **/ 249 TypeInfo type() { 250 return typeid(T); 251 } 252 } 253 254 /** 255 Adds an configurer to the PropertyConfigurersAware. 256 257 Params: 258 configurer = a configurer that will be invoked after factory of an object. 259 260 Returns: 261 The PropertyConfigurersAware instance 262 **/ 263 GenericFactory!T addPropertyConfigurer(PropertyConfigurer!T configurer) { 264 265 this.configurers ~= configurer; 266 267 return this; 268 } 269 } 270 271 } 272 273 /** 274 ditto 275 **/ 276 GenericFactory!T genericFactory(T)(Locator!() locator) { 277 return new GenericFactoryImpl!T(locator); 278 } 279 280 /** 281 ParameterHolder Stores a set of Args for futher usage in it's subclasses. 282 283 Params: 284 Args = a type tuple of args that ParameterHolder can hold. 285 **/ 286 abstract class ParameterHolder(Args...) : LocatorAware!() { 287 288 protected { 289 Tuple!Args args_; 290 Locator!() locator_; 291 } 292 293 public { 294 295 static if (Args.length > 0) { 296 297 /** 298 Set args 299 300 Params: 301 args = arguments that parameter holder should hold. 302 Returns: 303 typeof(this) 304 **/ 305 ParameterHolder args(ref Args args) @safe nothrow { 306 this.args_ = tuple(args); 307 308 return this; 309 } 310 } 311 312 /** 313 Get args 314 315 Returns: 316 Tuple!Args arguments stored by argument holder 317 **/ 318 Tuple!Args args() @safe nothrow { 319 return this.args_; 320 } 321 322 @property { 323 /** 324 Sets the locator that will be used by configurer to fetch object referenced in argument list. 325 326 Params: 327 locator = the (service) locator that will be used to fetch required objects. 328 329 Returns: 330 The ParameterHolder instance. 331 **/ 332 ParameterHolder!Args locator(Locator!() locator) { 333 this.locator_ = locator; 334 335 return this; 336 } 337 338 /** 339 Get locator 340 341 Returns: 342 Locator!() 343 **/ 344 Locator!() locator() @safe nothrow { 345 return this.locator_; 346 } 347 } 348 } 349 } 350 351 /** 352 Calls aggregate's method with a set of arguments. 353 354 Encapsulates a call to aggregate's method, with a set of arguments. 355 The algorithm that calls aggregate's method, will automatically replace 356 references from args list with data extracted from container, casted to 357 type that is extracted from method's signature. 358 359 Params: 360 T = the aggregate type 361 property = method that will be called 362 Args = type tuple of args that method can be called with. 363 **/ 364 class MethodConfigurer(T, string property, Args...) : ParameterHolder!Args, PropertyConfigurer!T 365 if ( 366 isMethodCompatible!(T, property, Args) 367 ) { 368 369 public { 370 371 /** 372 Constructor for MethodConfigurer!(T, property, Args) 373 374 Params: 375 args = list of arguments passed to T's method 376 **/ 377 this(ref Args args) { 378 this.args(args); 379 } 380 381 /** 382 See PropertyConfigurer interface 383 384 Throws: 385 InvalidCastException when extracted data by reference, is not of type expected by argument 386 of aggregate's method 387 **/ 388 void configure(ref T obj) { 389 390 try { 391 392 alias ArgTuple = Parameters!( 393 Filter!( 394 partialSuffixed!( 395 isArgumentListCompatible, 396 Args 397 ), 398 getOverloads!( 399 T, 400 property 401 ) 402 )[0] 403 ); 404 Tuple!ArgTuple parameters; 405 406 foreach (index, ref parameter; parameters) { 407 408 parameter = args[index].resolve!(typeof(parameter))(this.locator); 409 } 410 __traits(getMember, obj, property)(parameters.expand); 411 } catch (Exception e) { 412 throw new PropertyConfigurerException("Error occurred during call of " ~ name!T ~ "." ~ property, e); 413 } 414 } 415 } 416 } 417 418 /** 419 ditto 420 **/ 421 auto methodConfigurer(string property, T, Args...)(Locator!() locator, auto ref Args args) 422 if (!isField!(T, property)) { 423 mixin assertObjectMethodCompatible!(T, property, Args); 424 425 auto propertySetter = new MethodConfigurer!(T, property, Args)(args); 426 propertySetter.locator = locator; 427 428 return propertySetter; 429 } 430 431 /** 432 Sets aggregate's field to a value. 433 434 Encapsulates logic that sets aggregates field to a certain value. 435 If argument that is contained by configurer is a reference, it will be automatically 436 replaced with value extracted from locator, and set to aggregate's field. 437 **/ 438 class FieldConfigurer(T, string property, Arg) : ParameterHolder!Arg, PropertyConfigurer!T 439 if ( 440 isFieldCompatible!(T, property, Arg) 441 ) { 442 443 public { 444 /** 445 Constructor for MethodConfigurer!(T, property, Args) 446 447 Params: 448 arg = list of arguments passed to T's method 449 **/ 450 this(ref Arg arg) { 451 this.args(arg); 452 } 453 454 /** 455 See PropertyConfigurer interface 456 457 Throws: 458 InvalidCastException when extracted data by reference, is not of type expected by argument 459 of aggregate's field 460 **/ 461 void configure(ref T obj) { 462 463 try { 464 465 __traits(getMember, obj, property) = args[0].resolve!( 466 typeof(__traits(getMember, obj, property)) 467 )( 468 this.locator 469 ); 470 } catch (Exception e) { 471 472 throw new PropertyConfigurerException("Error occurred during set of " ~ name!T ~ "." ~ property, e); 473 } 474 } 475 } 476 } 477 478 /** 479 ditto 480 **/ 481 auto fieldConfigurer(string property, T, Arg)(Locator!() locator, auto ref Arg arg) 482 if (isField!(T, property)) { 483 mixin assertFieldCompatible!(T, property, Arg); 484 485 auto propertySetter = new FieldConfigurer!(T, property, Arg)(arg); 486 propertySetter.locator = locator; 487 488 return propertySetter; 489 } 490 491 /** 492 Instantiates an aggregate using it's constructor with no arguments. 493 **/ 494 class DefaultConstructorBasedFactory(T) : InstanceFactory!T 495 if ( 496 hasDefaultCtor!T 497 ) { 498 499 public { 500 /** 501 Create a new instance of object of type T. 502 503 Returns: 504 T instantiated component 505 **/ 506 T factory() { 507 508 try { 509 static if (is(T : Object)) { 510 return new T(); 511 } else { 512 return T.init; 513 } 514 } catch (Exception e) { 515 throw new InstanceFactoryException("Error occurred during instantiation of " ~ name!T, e); 516 } 517 } 518 } 519 } 520 521 /** 522 Instantiates aggregate using it's constructor with args. 523 524 Encapsulates construction of aggregate using a constructor, with args. 525 Arguments from argument list that are references, are automatically 526 replaced with data extracted from locator. 527 528 Params: 529 T = aggregate type 530 Args = type tuple of args that are passed to T's constructor 531 **/ 532 class ConstructorBasedFactory(T, Args...) : ParameterHolder!Args, InstanceFactory!T 533 if ( 534 isObjectConstructorCompatible!(T, Args) 535 ) { 536 537 public { 538 /** 539 Constructor for ConstructorBasedFactory!(T, Args) 540 541 Params: 542 args = arguments used for constructor 543 **/ 544 this(ref Args args) { 545 this.args(args); 546 } 547 548 /** 549 See InstanceFactory interface 550 551 Throws: 552 InvalidCastException when extracted data by reference, is not of type expected by argument 553 of aggregate's constructor 554 **/ 555 T factory() { 556 557 try { 558 559 alias ConstructorArgs = staticMap!(Unqual, 560 Parameters!( 561 Filter!( 562 partialSuffixed!( 563 isArgumentListCompatible, 564 Args 565 ), 566 __traits(getOverloads, T, "__ctor") 567 )[0] 568 ) 569 ); 570 571 Tuple!ConstructorArgs parameters; 572 573 foreach (index, ref parameter; parameters) { 574 parameter = this.args[index].resolve!(typeof(parameter))(this.locator); 575 } 576 577 static if (is(T : Object)) { 578 return new T(parameters.expand); 579 } else { 580 return T(parameters.expand); 581 } 582 } catch (Exception e) { 583 584 throw new InstanceFactoryException("Error occurred during instantiation of " ~ name!T, e); 585 } 586 } 587 } 588 } 589 590 /** 591 ditto 592 **/ 593 auto constructorBasedFactory(T, Args...)(Locator!() locator, auto ref Args args) { 594 mixin assertObjectConstructorCompatible!(T, Args); 595 auto constructor = new ConstructorBasedFactory!(T, Args)(args); 596 597 constructor.locator = locator; 598 599 return constructor; 600 } 601 602 /** 603 Instantiates an aggregate using a method from other aggregate (factory method pattern). 604 605 Encapsulates construction of aggregate using factory method. 606 Arguments that are references, will be replaced with data extracted 607 from locator, and passed to factory's method. 608 In case when method is not static member, the algorithm will use 609 an instantiaton of factory passed to it, or extracted from locator 610 if a reference is passed. 611 612 Params: 613 T = factory that is used to instantiate aggregate using it's method 614 method = the name of method used to instantiate aggregate 615 W = the factory T, or a LocatorReference to the factory. 616 Args = type tuple of arguments passed to factory. 617 **/ 618 class FactoryMethodBasedFactory(T, string method, W, Args...) : 619 ParameterHolder!Args, 620 InstanceFactory!(ReturnType!(getCompatibleOverload!(T, method, Args))) 621 if ( 622 (is(W : RuntimeReference) || is(W : T)) && 623 isMethodCompatible!(T, method, Args) && 624 isAggregateType!(ReturnType!(getCompatibleOverload!(T, method, Args))) 625 ) { 626 627 static if (!__traits(isStaticFunction, getCompatibleOverload!(T, method, Args))) { 628 629 private { 630 W fact; 631 } 632 633 public { 634 /** 635 Constructor for FactoryMethodBasedFactory!(T, method, W, Args) 636 637 Params: 638 fact = factory used to create object 639 args = arguments passed to factory's method 640 **/ 641 this(ref W fact, ref Args args) { 642 this.args(args); 643 this.fact = fact; 644 } 645 } 646 } else { 647 648 /** 649 Constructor for FactoryMethodBasedFactory!(T, method, W, Args) 650 651 Params: 652 args = arguments passed to factory's static method 653 **/ 654 this(ref Args args) { 655 this.args(args); 656 } 657 } 658 659 private { 660 661 alias Z = ReturnType!(getCompatibleOverload!(T, method, Args)); 662 } 663 664 public { 665 /** 666 See InstanceFactory interface 667 668 Throws: 669 InvalidCastException when extracted data by reference, is not of type expected by argument 670 factory's method, or when factory is referenced, and the object referenced in locator is not 671 of factory's type T. 672 Returns: 673 Z created object 674 **/ 675 Z factory() { 676 677 try { 678 679 alias FactoryMethodParameters = Parameters!( 680 Filter!( 681 partialSuffixed!( 682 isArgumentListCompatible, 683 Args 684 ), __traits( 685 getOverloads, 686 T, 687 method 688 ) 689 )[0] 690 ); 691 692 Tuple!FactoryMethodParameters parameters; 693 694 foreach (index, ref parameter; parameters) { 695 parameter = this.args[index].resolve!(typeof(parameter))(this.locator); 696 } 697 698 static if (!__traits(isStaticFunction, getCompatibleOverload!(T, method, Args))) { 699 700 return __traits(getMember, this.fact.resolve!(T)(this.locator), method)(parameters.expand); 701 } else { 702 703 return __traits(getMember, T, method)(parameters.expand); 704 } 705 } catch (Exception e) { 706 707 throw new InstanceFactoryException( 708 "Error occurred during instantiation of " ~ 709 name!T ~ 710 " using factory method of " 711 ~ name!T ~ 712 "." ~ 713 method, 714 e 715 ); 716 } 717 } 718 } 719 } 720 721 /** 722 ditto 723 **/ 724 auto factoryMethodBasedFactory 725 (T, string method, W, Args...) 726 (Locator!() locator, auto ref W factoryMethod, auto ref Args args) 727 if ( 728 isNonStaticMethodCompatible!(T, method, Args) && 729 (is(W : T) || is(W : RuntimeReference)) 730 ) { 731 732 auto constructor = new FactoryMethodBasedFactory!(T, method, W, Args)(factoryMethod, args); 733 734 constructor.locator = locator; 735 736 return constructor; 737 } 738 739 /** 740 ditto 741 **/ 742 auto factoryMethodBasedFactory(T, string method, Args...)(Locator!() locator, auto ref Args args) 743 if ( 744 isStaticMethodCompatible!(T, method, Args) 745 ) { 746 auto constructor = new FactoryMethodBasedFactory!(T, method, T, Args)(args); 747 748 constructor.locator = locator; 749 750 return constructor; 751 } 752 753 /** 754 Instantiates data of type T using a delegate or function. 755 756 Encapsulates data's construction logic using a delegate. 757 The algorithm uses a delegate to create required data, 758 with a set of Args that are passed to delegate, and a locator 759 for dependency fetching. 760 761 Params: 762 T = the constructed aggregate 763 Args = type tuple of arguments passed to delegate for aggregate's construction. 764 **/ 765 class CallbackFactory(T, Dg, Args...) : ParameterHolder!Args, InstanceFactory!T 766 if ((is(Dg == T delegate (Locator!(), Args)) || is(Dg == T function (Locator!(), Args)))) { 767 768 private { 769 Dg dg; 770 } 771 772 public { 773 /** 774 Constructor for CallbackFactory!(T, Dg, Args) 775 776 Params: 777 dg = delegate used to create object 778 args = arguments passed to delegate 779 **/ 780 this(Dg dg, ref Args args) { 781 this.dg = dg; 782 this.args(args); 783 } 784 785 /** 786 See InstanceFactory interface 787 **/ 788 T factory() { 789 try { 790 791 return this.dg(this.locator_, args.expand); 792 } catch (Exception e) { 793 794 throw new InstanceFactoryException( 795 "Error occurred during instantiation of " ~ 796 name!T ~ 797 " using callback factory", 798 e 799 ); 800 } 801 } 802 } 803 } 804 805 /** 806 ditto 807 **/ 808 auto callbackFactory(T, Args...)(Locator!() locator, T delegate(Locator!(), Args) dg, auto ref Args args) { 809 auto constr = new CallbackFactory!(T, T delegate(Locator!(), Args), Args)(dg, args); 810 constr.locator = locator; 811 return constr; 812 } 813 814 /** 815 ditto 816 **/ 817 auto callbackFactory(T, Args...)(Locator!() locator, T function(Locator!(), Args) dg, auto ref Args args) { 818 auto constr = new CallbackFactory!(T, T function(Locator!(), Args), Args)(dg, args); 819 constr.locator = locator; 820 return constr; 821 } 822 823 /** 824 Configures/modifies data of type T with help of a delegate or function. 825 826 Encapsulates data configuration logic using a delegate. 827 The algorithm calls delegate, with a locator, a set of Args, 828 and configured data, in order to modify the data 829 somehow. 830 831 Note: 832 If data is not a reference type it is recommended to pass it by reference 833 in order to avoid receiving of a copy and not original one in delegate. 834 Params: 835 T = the aggregate 836 Args = type tuple of arguments used by delegate for customization. 837 **/ 838 class CallbackConfigurer(T, Dg, Args...) : ParameterHolder!Args, PropertyConfigurer!T 839 if ( 840 is(Dg == void delegate (Locator!(), T, Args)) || 841 is(Dg == void function (Locator!(), T, Args)) || 842 is(Dg == void delegate (Locator!(), ref T, Args)) || 843 is(Dg == void function (Locator!(), ref T, Args)) 844 ) { 845 846 private { 847 Dg dg; 848 } 849 850 public { 851 /** 852 Constructor for CallbackConfigurer!(T, Dg, Args) 853 854 Params: 855 dg = delegate used to configure the created object 856 args = arguments passed to delegate 857 **/ 858 this(Dg dg, ref Args args) { 859 this.dg = dg; 860 this.args(args); 861 } 862 863 /** 864 Accepts a reference to an object that is to be configured by the configurer. 865 866 Params: 867 object = An object of type T, that will be configured 868 **/ 869 void configure(ref T object) { 870 871 try { 872 873 return this.dg(this.locator_, object, args.expand); 874 } catch (Exception e) { 875 throw new PropertyConfigurerException("Error occurred during callback configuration of " ~ name!T, e); 876 } 877 } 878 } 879 } 880 881 /** 882 ditto 883 **/ 884 auto callbackConfigurer(T, Args...)(Locator!() locator, void delegate(Locator!(), T, Args) dg, auto ref Args args) { 885 auto constr = new CallbackConfigurer!(T, void delegate(Locator!(), T, Args), Args)(dg, args); 886 constr.locator = locator; 887 return constr; 888 } 889 890 /** 891 ditto 892 **/ 893 auto callbackConfigurer(T, Args...)(Locator!() locator, void function(Locator!(), T, Args) dg, auto ref Args args) { 894 auto constr = new CallbackConfigurer!(T, void function(Locator!(), T, Args), Args)(dg, args); 895 constr.locator = locator; 896 return constr; 897 } 898 899 /** 900 ditto 901 **/ 902 auto callbackConfigurer(T, Args...)(Locator!() locator, void delegate(Locator!(), ref T, Args) dg, auto ref Args args) { 903 auto constr = new CallbackConfigurer!(T, void delegate(Locator!(), ref T, Args), Args)(dg, args); 904 constr.locator = locator; 905 return constr; 906 } 907 908 /** 909 ditto 910 **/ 911 auto callbackConfigurer(T, Args...)(Locator!() locator, void function(Locator!(), ref T, Args) dg, auto ref Args args) { 912 auto constr = new CallbackConfigurer!(T, void function(Locator!(), ref T, Args), Args)(dg, args); 913 constr.locator = locator; 914 return constr; 915 } 916 917 918 /** 919 Instantiates a component using a value as basis. 920 921 Instantiates a component using a value as basis. 922 As a consequence, any reference based type will 923 point to same content when it is instantiated 924 multiple times. 925 **/ 926 class ValueInstanceFactory(T) : InstanceFactory!T { 927 private { 928 T initial_; 929 } 930 931 public { 932 /** 933 Constructor for ValueInstanceFactory!T 934 935 Params: 936 initial = argument that is to be passed as created object 937 **/ 938 this(T initial) { 939 this.initial = initial; 940 } 941 942 @property { 943 944 /** 945 Set initial 946 947 Params: 948 initial = value which will be passed as created component 949 Returns: 950 typeof(this) 951 **/ 952 ValueInstanceFactory!T initial(T initial) @safe nothrow { 953 this.initial_ = initial; 954 955 return this; 956 } 957 958 /** 959 Get initial 960 961 Returns: 962 T 963 **/ 964 T initial() @safe nothrow { 965 return this.initial_; 966 } 967 } 968 969 /** 970 Create a new instance of object of type T. 971 972 Returns: 973 T instantiated component 974 **/ 975 T factory() { 976 return this.initial(); 977 } 978 } 979 } 980 981 /** 982 InstanceFactory that delegates the task of instantiating a component 983 to some third party factory. 984 **/ 985 class DelegatingInstanceFactory(T, X : T) : InstanceFactory!T, MutableDecorator!(Factory!X) { 986 987 private { 988 Factory!X decorated_; 989 } 990 991 public { 992 993 /** 994 Default constructor for DelegatingInstanceFactory!(T, X) 995 **/ 996 this() { 997 998 } 999 1000 /** 1001 Constructor for DelegatingInstanceFactory!(T, X) 1002 1003 Params: 1004 factory = the factory to which this instance will delegate the task of creating a component 1005 **/ 1006 this(Factory!X factory) { 1007 this.decorated = factory; 1008 } 1009 1010 @property { 1011 /** 1012 Set the decorated object for decorator. 1013 1014 Params: 1015 decorated = decorated data 1016 1017 Returns: 1018 this 1019 **/ 1020 DelegatingInstanceFactory!(T, X) decorated(Factory!X decorated) @safe nothrow { 1021 this.decorated_ = decorated; 1022 1023 return this; 1024 } 1025 1026 /** 1027 Get the decorated object. 1028 1029 Returns: 1030 T decorated object 1031 **/ 1032 Factory!X decorated() @safe nothrow { 1033 return this.decorated_; 1034 } 1035 } 1036 1037 /** 1038 Create a new instance of object of type T. 1039 1040 Returns: 1041 T instantiated component 1042 **/ 1043 T factory() { 1044 return this.decorated.factory(); 1045 } 1046 } 1047 } 1048 1049 /** 1050 An check if the argument list passed to ConstructorBasedFactory or MethodConfigurer is compatible with signature of underlying 1051 method or constructor. 1052 1053 Note: 1054 For now it checks if the lengths are equal. For future it should also check if types are compatible. 1055 **/ 1056 template isArgumentListCompatible(alias func, ArgTuple...) 1057 if (isSomeFunction!func) { 1058 bool isArgumentListCompatible() { 1059 alias FuncParams = Parameters!func; 1060 alias Required = Filter!(partialSuffixed!(isValueOfType, void), ParameterDefaults!func); 1061 1062 static if ((ArgTuple.length < Required.length) || (ArgTuple.length > FuncParams.length)) { 1063 1064 return false; 1065 } else { 1066 1067 bool result = true; 1068 foreach (index, Argument; ArgTuple) { 1069 1070 static if (!is(Argument : RuntimeReference) && !isImplicitlyConvertible!(Argument, FuncParams[index])) { 1071 1072 result = false; 1073 break; 1074 } 1075 } 1076 1077 return result; 1078 } 1079 } 1080 } 1081 1082 mixin template assertFieldCompatible(T, string field, Arg) { 1083 import aermicioi.util.traits; 1084 import std.traits; 1085 import std.meta; 1086 1087 static assert(isField!(T, field), name!T ~ "'s " ~ field ~ " member is not a field"); 1088 static assert(isProtection!(T, field, "public"), name!T ~ "'s " ~ field ~ " is not public and therefore cannot be accessed."); 1089 static assert(is(Arg : RuntimeReference) ? true : isImplicitlyConvertible!(Arg, typeof(getMember!(T, field))), name!T ~"'s " ~ field ~ " type " ~ name!(typeof(getMember!(T, field))) ~ " doesn't match with passed arguments type " ~ name!Arg); 1090 } 1091 1092 enum bool isFieldCompatible(T, string field, Arg) = 1093 isField!(T, field) && 1094 isProtection!(T, field, "public") && 1095 is(Arg : RuntimeReference) ? true : isImplicitlyConvertible!(Arg, typeof(getMember!(T, field))); 1096 1097 mixin template assertObjectConstructorCompatible(T, Args...) { 1098 import aermicioi.util.traits; 1099 import std.traits; 1100 import std.meta; 1101 1102 static assert(hasMember!(T, "__ctor"), name!T ~ " doesn't have any constructor to call."); 1103 static assert(isProtection!(T, "__ctor", "public"), name!T ~ "'s constructor is not public."); 1104 static assert(isSomeFunction!(__traits(getMember, T, "__ctor")), name!T ~ "'s constructor is not a function, probably a template."); 1105 static assert(variadicFunctionStyle!(__traits(getMember, T, "__ctor")) == Variadic.no, name!T ~ "'s constructor is a variadic function. Only non-variadic constructors are supported."); 1106 static assert(Filter!(partialSuffixed!(isArgumentListCompatible, Args), __traits(getOverloads, T, "__ctor")).length == 1, "None, or multiple overloads found for " ~ name!T ~ "'s constructor with passed arguments."); 1107 } 1108 1109 enum bool isObjectConstructorCompatible(T, Args...) = isMethodCompatible!(T, "__ctor", Args); 1110 1111 mixin template assertObjectMethodCompatible(T, string method, Args...) { 1112 import std.range : only; 1113 import std.array : array; 1114 import aermicioi.util.traits; 1115 import std.traits; 1116 import std.meta; 1117 1118 static assert(hasMember!(T, method), name!T ~ "'s method " ~ method ~ " not found."); 1119 static assert(isProtection!(T, method, "public"), name!T ~ "'s method " ~ method ~ " is not public"); 1120 static assert(isSomeFunction!(__traits(getMember, T, method)), name!T ~ "'s member " ~ method ~ " is not a function, probably a field, or a template."); 1121 static assert(variadicFunctionStyle!(__traits(getMember, T, method)) == Variadic.no, name!T ~ "'s method " ~ method ~ "is variadic function. Only non-variadic methods are supported."); 1122 static assert(Filter!(partialSuffixed!(isArgumentListCompatible, Args), getOverloads!(T, method)).length == 1, name!T ~ "'s " ~ method ~ " doesn't have overload matching passed arguments (" ~ only(staticMap!(name, Args)).joiner(", ").array ~ "), or has several overloads that match."); 1123 } 1124 1125 enum bool isObjectMethodCompatible(T, string method, Args...) = isMethodCompatible!(T, method, Args); 1126 1127 1128 template isMethodCompatible(T, string method, Args...) { 1129 enum bool isMethodCompatible = 1130 hasMember!(T, method) && 1131 isProtection!(T, method, "public") && 1132 isSomeFunction!(__traits(getMember, T, method)) && 1133 (variadicFunctionStyle!(__traits(getMember, T, method)) == Variadic.no) && 1134 (Filter!(partialSuffixed!(isArgumentListCompatible, Args), getOverloads!(T, method)).length == 1); 1135 } 1136 1137 template getCompatibleOverload(T, string method, Args...) 1138 if (isObjectMethodCompatible!(T, method, Args)) { 1139 1140 alias getCompatibleOverload = Filter!(partialSuffixed!(isArgumentListCompatible, Args), getOverloads!(T, method))[0]; 1141 } 1142 1143 alias isStaticMethodCompatible = templateAnd!( 1144 isMethodCompatible, 1145 chain!( 1146 isStaticFunction, 1147 getCompatibleOverload 1148 ) 1149 ); 1150 1151 alias isNonStaticMethodCompatible = templateAnd!( 1152 isMethodCompatible, 1153 chain!( 1154 templateNot!isStaticFunction, 1155 getCompatibleOverload 1156 ) 1157 ); 1158 1159 alias isNonStaticMethodCompatibleAndReturnTypeOf(X) = templateAnd!( 1160 isMethodCompatible, 1161 chain!( 1162 templateNot!isStaticFunction, 1163 getCompatibleOverload 1164 ), 1165 partialSuffixed!( 1166 chain!( 1167 partialPrefixed!( 1168 get, 1169 0 1170 ), 1171 getCompatibleOverload 1172 ), 1173 X 1174 ) 1175 ); 1176 1177 private { 1178 template isValueOfType(alias value, Type) { 1179 enum bool isValueOfType = is(typeof(value) == Type); 1180 } 1181 1182 template isValueOfType(Value, Type) { 1183 enum bool isValueOfType = is(Value == Type); 1184 } 1185 1186 enum bool isStruct(T) = is(T == struct); 1187 1188 alias hasDefaultCtor = 1189 partialSuffixed!( 1190 templateOr!( 1191 templateNot!hasMember, 1192 chain!( 1193 isStruct, 1194 get!0 1195 ), 1196 templateAnd!( 1197 partialSuffixed!( 1198 isProtection, 1199 "public" 1200 ), 1201 chain!( 1202 partialPrefixed!( 1203 anySatisfy, 1204 eq!0 1205 ), 1206 partialPrefixed!( 1207 staticMap, 1208 arity 1209 ), 1210 chain!( 1211 partialPrefixed!( 1212 Filter, 1213 partialSuffixed!( 1214 isProtection, 1215 "public" 1216 ) 1217 ), 1218 getOverloads 1219 ) 1220 ) 1221 ) 1222 ), 1223 "__ctor" 1224 ); 1225 }