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 import aermicioi.aedi.factory.factory; 34 import aermicioi.aedi.factory.reference; 35 36 import aermicioi.aedi.storage.locator; 37 import aermicioi.aedi.storage.locator_aware; 38 import aermicioi.aedi.exception; 39 import aermicioi.aedi.storage.wrapper; 40 import aermicioi.util.traits; 41 42 import std.typecons; 43 import std.traits; 44 import std.meta; 45 import std.conv : to; 46 47 /** 48 A property configurer, has the purpose to modify data of type T according to some logic encapsulated in it. 49 50 **/ 51 interface PropertyConfigurer(T) { 52 53 public { 54 55 /** 56 Accepts a reference to an object that is to be configured by the configurer. 57 58 Params: 59 object = An object of type T, that will be configured 60 **/ 61 void configure(ref T object); 62 } 63 } 64 65 /** 66 An instance factory, instantiates data of type T. 67 **/ 68 interface InstanceFactory(T) { 69 70 public { 71 72 /** 73 Create a new instance of object of type T. 74 **/ 75 T factory(); 76 } 77 } 78 79 /** 80 A generic factory, is a factory that instantiates data of type T using InstanceFactory and a list of PropertyConfigurers. 81 82 A generic factory, is a factory that instantiates data of type T using InstanceFactory and a list of PropertyConfigurers. 83 It can optionally provide a Locator!() object to InstanceFactory and PropertyConfigurer to be used as a source of data. 84 **/ 85 interface GenericFactory(T) : Factory!T { 86 87 public { 88 89 @property { 90 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 GenericFactoryInstance 99 **/ 100 GenericFactory!T setInstanceFactory(InstanceFactory!T factory); 101 102 /** 103 Get the GenericFactory locator. 104 105 Returns: 106 Locator!() the locator that should be used by underlying constructor or property configurer. 107 **/ 108 Locator!() locator(); 109 } 110 111 /** 112 Adds an configurer to the GenericFactory. 113 114 Params: 115 configurer = a configurer that will be invoked after factory of an object. 116 117 Returns: 118 The GenericFactoryInstance 119 **/ 120 GenericFactory!T addPropertyConfigurer(PropertyConfigurer!T configurer); 121 } 122 } 123 124 /** 125 A concrete implementation of GenericFactory interface. 126 **/ 127 class GenericFactoryImpl(T) : GenericFactory!T, LocatorAware!() { 128 129 private { 130 Locator!() locator_; 131 132 InstanceFactory!T factory_; 133 134 PropertyConfigurer!T[] configurers; 135 } 136 137 public { 138 139 this(Locator!() locator) { 140 this.locator = locator; 141 142 static if (hasDefaultCtor!T) { 143 144 this.setInstanceFactory( 145 new DefaultConstructorBasedFactory!T 146 ); 147 } 148 } 149 150 T factory() { 151 T instance; 152 153 if (this.factory_ !is null) { 154 instance = this.factory_.factory; 155 } else { 156 157 throw new AediException("Failed to construct object due to no constructor"); 158 } 159 160 foreach (key, configurer; this.configurers) { 161 configurer.configure(instance); 162 } 163 164 return instance; 165 } 166 167 @property { 168 169 GenericFactory!T setInstanceFactory(InstanceFactory!T factory) { 170 this.factory_ = factory; 171 172 return this; 173 } 174 175 GenericFactoryImpl!T locator(Locator!() locator) { 176 this.locator_ = locator; 177 return this; 178 } 179 180 Locator!() locator() { 181 return this.locator_; 182 } 183 184 /** 185 Get the type info of object that is created. 186 187 Returns: 188 TypeInfo object of created object. 189 **/ 190 TypeInfo type() { 191 return typeid(T); 192 } 193 } 194 195 GenericFactory!T addPropertyConfigurer(PropertyConfigurer!T configurer) { 196 197 this.configurers ~= configurer; 198 199 return this; 200 } 201 } 202 203 } 204 205 /** 206 ParameterHolder Stores a set of Args for futher usage in it's subclasses. 207 208 Params: 209 Args = a type tuple of args that ParameterHolder can hold. 210 **/ 211 abstract class ParameterHolder(Args...) : LocatorAware!() { 212 213 protected { 214 Tuple!Args args_; 215 Locator!() locator_; 216 } 217 218 public { 219 220 static if (Args.length > 0) { 221 ParameterHolder args(ref Args args) @safe nothrow { 222 this.args_ = tuple(args); 223 224 return this; 225 } 226 } 227 228 Tuple!Args args() @safe nothrow { 229 return this.args_; 230 } 231 232 @property { 233 /** 234 Sets the locator that will be used by configurer to fetch object referenced in argument list. 235 236 Params: 237 locator = the (service) locator that will be used to fetch required objects. 238 239 Returns: 240 The ParameterHolder instance. 241 **/ 242 ParameterHolder!Args locator(Locator!() locator) { 243 this.locator_ = locator; 244 245 return this; 246 } 247 248 Locator!() locator() @safe nothrow { 249 return this.locator_; 250 } 251 } 252 } 253 } 254 255 /** 256 Calls aggregate's method with a set of arguments. 257 258 Encapsulates a call to aggregate's method, with a set of arguments. 259 The algorithm that calls aggregate's method, will automatically replace 260 references from args list with data extracted from container, casted to 261 type that is extracted from method's signature. 262 263 Params: 264 T = the aggregate type 265 property = method that will be called 266 Args = type tuple of args that method can be called with. 267 **/ 268 class MethodConfigurer(T, string property, Args...) : ParameterHolder!Args, PropertyConfigurer!T 269 if ( 270 isMethodCompatible!(T, property, Args) 271 ) { 272 273 public { 274 275 this(ref Args args) { 276 this.args(args); 277 } 278 279 /** 280 See PropertyConfigurer interface 281 282 Throws: 283 InvalidCastException when extracted data by reference, is not of type expected by argument 284 of aggregate's method 285 **/ 286 void configure(ref T obj) { 287 288 try { 289 290 alias ArgTuple = Parameters!(Filter!(partialSuffixed!(isArgumentListCompatible, Args), getOverloads!(T, property))[0]); 291 Tuple!ArgTuple parameters; 292 293 foreach (index, ref parameter; parameters) { 294 295 parameter = args[index].resolve!(typeof(parameter))(this.locator); 296 } 297 __traits(getMember, obj, property)(parameters.expand); 298 } catch (Exception e) { 299 throw new PropertyConfigurerException("Error occurred during call of " ~ name!T ~ "." ~ property, e); 300 } 301 } 302 } 303 } 304 305 /** 306 Invoke aggregate's method with supplied args. 307 308 Configures aggregate's factory to call specified method with passed args. 309 The function will check if the arguments passed to it are compatible with at 310 least one method from possible overload set. 311 The args list can contain references to other objects in locator as well, though 312 no type compatibility checks will be performed at compile time. 313 314 Params: 315 factory = the factory which will be configured to invoke method. 316 args = the arguments that will be used to invoke method on the new object. 317 318 Returns: 319 GenericFactory!T. 320 **/ 321 auto methodConfigurer(string property, T, Args...)(Locator!() locator, auto ref Args args) 322 if (!isField!(T, property)) { 323 mixin assertObjectMethodCompatible!(T, property, Args); 324 325 auto propertySetter = new MethodConfigurer!(T, property, Args)(args); 326 propertySetter.locator = locator; 327 328 return propertySetter; 329 } 330 331 /** 332 Sets aggregate's field to a value. 333 334 Encapsulates logic that sets aggregates field to a certain value. 335 If argument that is contained by configurer is a reference, it will be automatically 336 replaced with value extracted from locator, and set to aggregate's field. 337 **/ 338 class FieldConfigurer(T, string property, Arg) : ParameterHolder!Arg, PropertyConfigurer!T 339 if ( 340 isFieldCompatible!(T, property, Arg) 341 ) { 342 343 public { 344 345 this(ref Arg arg) { 346 this.args(arg); 347 } 348 349 /** 350 See PropertyConfigurer interface 351 352 Throws: 353 InvalidCastException when extracted data by reference, is not of type expected by argument 354 of aggregate's field 355 **/ 356 void configure(ref T obj) { 357 358 try { 359 360 __traits(getMember, obj, property) = args[0].resolve!(typeof(__traits(getMember, obj, property)))(this.locator); 361 } catch (Exception e) { 362 363 throw new PropertyConfigurerException("Error occurred during set of " ~ name!T ~ "." ~ property, e); 364 } 365 } 366 } 367 } 368 369 /** 370 Set aggregate's public field to passed arg. 371 372 Configures aggregate's factory to set specified field to passed arg. 373 The function will check if passed argument is type compatible with specified field. 374 The argument can be a reference as well. In case of argument being reference to another data 375 in container, no type compatiblity checking will be done. 376 377 Params 378 factory = the factory which will be configured to set property. 379 arg = the value of property to be set, or reference to data in container. 380 381 Returns: 382 GenericFactory!T. 383 **/ 384 auto fieldConfigurer(string property, T, Arg)(Locator!() locator, auto ref Arg arg) 385 if (isField!(T, property)) { 386 mixin assertFieldCompatible!(T, property, Arg); 387 388 auto propertySetter = new FieldConfigurer!(T, property, Arg)(arg); 389 propertySetter.locator = locator; 390 391 return propertySetter; 392 } 393 394 /** 395 Instantiates an aggregate using it's constructor with no arguments. 396 **/ 397 class DefaultConstructorBasedFactory(T) : InstanceFactory!T 398 if ( 399 hasDefaultCtor!T 400 ) { 401 402 public { 403 404 T factory() { 405 406 try { 407 static if (is(T : Object)) { 408 return new T(); 409 } else { 410 return T.init; 411 } 412 } catch (Exception e) { 413 throw new InstanceFactoryException("Error occurred during instantiation of " ~ name!T, e); 414 } 415 } 416 } 417 } 418 419 /** 420 Instantiates aggregate using it's constructor with args. 421 422 Encapsulates construction of aggregate using a constructor, with args. 423 Arguments from argument list that are references, are automatically 424 replaced with data extracted from locator. 425 426 Params: 427 T = aggregate type 428 Args = type tuple of args that are passed to T's constructor 429 **/ 430 class ConstructorBasedFactory(T, Args...) : ParameterHolder!Args, InstanceFactory!T 431 if ( 432 isObjectConstructorCompatible!(T, Args) 433 ) { 434 435 public { 436 437 this(ref Args args) { 438 this.args(args); 439 } 440 441 /** 442 See InstanceFactory interface 443 444 Throws: 445 InvalidCastException when extracted data by reference, is not of type expected by argument 446 of aggregate's constructor 447 **/ 448 T factory() { 449 450 try { 451 452 alias ConstructorArgs = Parameters!(Filter!(partialSuffixed!(isArgumentListCompatible, Args), __traits(getOverloads, T, "__ctor"))[0]); 453 454 Tuple!ConstructorArgs parameters; 455 456 foreach (index, ref parameter; parameters) { 457 parameter = this.args[index].resolve!(typeof(parameter))(this.locator); 458 } 459 460 static if (is(T : Object)) { 461 return new T(parameters.expand); 462 } else { 463 return T(parameters.expand); 464 } 465 } catch (Exception e) { 466 467 throw new InstanceFactoryException("Error occurred during instantiation of " ~ name!T, e); 468 } 469 } 470 } 471 } 472 473 /** 474 Construct aggregate using args. 475 476 Constructs aggregate using args, that are passed to function. 477 The function will attempt to find at least one construct that 478 can accept passed argument list. If it fails, compiler will 479 produce error, with respective problems. 480 The argument list can contain beside simple values, references 481 to other data in locator. Arguments that are references to other data 482 won't be type checked. 483 484 Params: 485 factory = the factory which will call constructor with passed arguments. 486 args = a list of arguments that will be passed to constructor. 487 488 Returns: 489 GenericFactory!T. 490 **/ 491 492 auto constructorBasedFactory(T, Args...)(Locator!() locator, auto ref Args args) { 493 mixin assertObjectConstructorCompatible!(T, Args); 494 auto constructor = new ConstructorBasedFactory!(T, Args)(args); 495 496 constructor.locator = locator; 497 498 return constructor; 499 } 500 501 /** 502 Instantiates an aggregate using a method from other aggregate (factory method pattern). 503 504 Encapsulates construction of aggregate using factory method. 505 Arguments that are references, will be replaced with data extracted 506 from locator, and passed to factory's method. 507 In case when method is not static member, the algorithm will use 508 an instantiaton of factory passed to it, or extracted from locator 509 if a reference is passed. 510 511 Params: 512 T = factory that is used to instantiate aggregate using it's method 513 method = the name of method used to instantiate aggregate 514 W = the factory T, or a LocatorReference to the factory. 515 Args = type tuple of arguments passed to factory. 516 **/ 517 class FactoryMethodBasedFactory(T, string method, W, Args...) : ParameterHolder!Args, InstanceFactory!(ReturnType!(getCompatibleOverload!(T, method, Args))) 518 if ( 519 (is(W : LocatorReference) || is(W : T)) && 520 isMethodCompatible!(T, method, Args) && 521 isAggregateType!(ReturnType!(getCompatibleOverload!(T, method, Args))) 522 ) { 523 524 static if (!__traits(isStaticFunction, getCompatibleOverload!(T, method, Args))) { 525 526 private { 527 W fact; 528 } 529 530 public { 531 this(ref W fact, ref Args args) { 532 this.args(args); 533 this.fact = fact; 534 } 535 } 536 } else { 537 538 this(ref Args args) { 539 this.args(args); 540 } 541 } 542 543 private { 544 545 alias Z = ReturnType!(getCompatibleOverload!(T, method, Args)); 546 } 547 548 public { 549 /** 550 See InstanceFactory interface 551 552 Throws: 553 InvalidCastException when extracted data by reference, is not of type expected by argument 554 factory's method, or when factory is referenced, and the object referenced in locator is not 555 of factory's type T. 556 **/ 557 Z factory() { 558 559 try { 560 561 alias FactoryMethodParameters = Parameters!(Filter!(partialSuffixed!(isArgumentListCompatible, Args), __traits(getOverloads, T, method))[0]); 562 563 Tuple!FactoryMethodParameters parameters; 564 565 foreach (index, ref parameter; parameters) { 566 parameter = this.args[index].resolve!(typeof(parameter))(this.locator); 567 } 568 569 static if (!__traits(isStaticFunction, getCompatibleOverload!(T, method, Args))) { 570 571 return __traits(getMember, this.fact.resolve!(T)(this.locator), method)(parameters.expand); 572 } else { 573 574 return __traits(getMember, T, method)(parameters.expand); 575 } 576 } catch (Exception e) { 577 578 throw new InstanceFactoryException("Error occurred during instantiation of " ~ name!T ~ " using factory method of " ~ name!T ~ "." ~ method, e); 579 } 580 } 581 } 582 } 583 584 /** 585 Invoke T's method to create data of type X. 586 587 Configures aggregate's factory to call method of factoryMethod with args, 588 in order to create data of type X. 589 In case when method is not a static member, the function requires to 590 pass a instance of factoryMethod or a reference to it. 591 The algorithm will check for args compatiblity with parameters of 592 factory method. No type check is done for arguments that are references 593 at compile time. 594 595 Params: 596 factory = aggregate's factory that is configured to call factoryMethod methods to spawn aggregate 597 factoryMethod = instance of factory method that will be used to instantiate aggregate 598 args = a list of arguments passed to factory method 599 T = type of factoryMethod 600 method = the method that is called from T to instantiate aggregate 601 W = either LocatorReference or T 602 X = the return type of T.method member 603 **/ 604 auto factoryMethodBasedFactory(T, string method, W, Args...)(Locator!() locator, auto ref W factoryMethod, auto ref Args args) 605 if ( 606 isNonStaticMethodCompatible!(T, method, Args) && 607 (is(W : T) || is(W : LocatorReference)) 608 ) { 609 610 auto constructor = new FactoryMethodBasedFactory!(T, method, W, Args)(factoryMethod, args); 611 612 constructor.locator = locator; 613 614 return constructor; 615 } 616 617 /** 618 ditto 619 **/ 620 auto factoryMethodBasedFactory(T, string method, Args...)(Locator!() locator, auto ref Args args) 621 if ( 622 isStaticMethodCompatible!(T, method, Args) 623 ) { 624 auto constructor = new FactoryMethodBasedFactory!(T, method, T, Args)(args); 625 626 constructor.locator = locator; 627 628 return constructor; 629 } 630 631 /** 632 Instantiates data of type T using a delegate or function. 633 634 Encapsulates data's construction logic using a delegate. 635 The algorithm uses a delegate to create required data, 636 with a set of Args that are passed to delegate, and a locator 637 for dependency fetching. 638 639 Params: 640 T = the constructed aggregate 641 Args = type tuple of arguments passed to delegate for aggregate's construction. 642 **/ 643 class CallbackFactory(T, Dg, Args...) : ParameterHolder!Args, InstanceFactory!T 644 if ((is(Dg == T delegate (Locator!(), Args)) || is(Dg == T function (Locator!(), Args)))) { 645 646 private { 647 Dg dg; 648 } 649 650 public { 651 this(Dg dg, ref Args args) { 652 this.dg = dg; 653 this.args(args); 654 } 655 656 /** 657 See InstanceFactory interface 658 **/ 659 T factory() { 660 try { 661 662 return this.dg(this.locator_, args.expand); 663 } catch (Exception e) { 664 665 throw new InstanceFactoryException("Error occurred during instantiation of " ~ name!T ~ " using callback factory", e); 666 } 667 } 668 } 669 } 670 671 /** 672 Construct aggregate using a delegate. 673 674 Constructs aggregate using a delegate, and a list of arguments passed to delegate. 675 676 Params: 677 factory = the factory which will use delegate to construct aggregate. 678 dg = the delegate that is responsible for creating aggregate, given a list of arguments. 679 args = the arguments that will be used by delegate to construct aggregate. 680 681 Returns: 682 GenericFactory!T. 683 **/ 684 auto callbackFactory(T, Args...)(Locator!() locator, T delegate(Locator!(), Args) dg, auto ref Args args) { 685 auto constr = new CallbackFactory!(T, T delegate(Locator!(), Args), Args)(dg, args); 686 constr.locator = locator; 687 return constr; 688 } 689 690 /** 691 ditto 692 **/ 693 auto callbackFactory(T, Args...)(Locator!() locator, T function(Locator!(), Args) dg, auto ref Args args) { 694 auto constr = new CallbackFactory!(T, T function(Locator!(), Args), Args)(dg, args); 695 constr.locator = locator; 696 return constr; 697 } 698 699 /** 700 Configures/modifies data of type T with help of a delegate or function. 701 702 Encapsulates data configuration logic using a delegate. 703 The algorithm calls delegate, with a locator, a set of Args, 704 and configured data, in order to modify the data 705 somehow. 706 707 Note: 708 If data is not a reference type it is recommended to pass it by reference 709 in order to avoid receiving of a copy and not original one in delegate. 710 Params: 711 T = the aggregate 712 Args = type tuple of arguments used by delegate for customization. 713 **/ 714 class CallbackConfigurer(T, Dg, Args...) : ParameterHolder!Args, PropertyConfigurer!T 715 if ( 716 is(Dg == void delegate (Locator!(), T, Args)) || 717 is(Dg == void function (Locator!(), T, Args)) || 718 is(Dg == void delegate (Locator!(), ref T, Args)) || 719 is(Dg == void function (Locator!(), ref T, Args)) 720 ) { 721 722 private { 723 Dg dg; 724 } 725 726 /** 727 See InstanceFactory interface 728 **/ 729 public { 730 this(Dg dg, ref Args args) { 731 this.dg = dg; 732 this.args(args); 733 } 734 735 void configure(ref T object) { 736 737 try { 738 739 return this.dg(this.locator_, object, args.expand); 740 } catch (Exception e) { 741 throw new PropertyConfigurerException("Error occurred during callback configuration of " ~ name!T, e); 742 } 743 } 744 } 745 } 746 747 /** 748 Call dg on an aggregate that is in configuration phase. 749 750 Call dg on aggregate to perform some modifications, using args as input. 751 752 Params: 753 factory = factory which will call dg with args. 754 dg = delegate that will perform some modifications on aggregate using passed args. 755 args = a list of arguments passed to dg. 756 757 Returns: 758 GenericFactory!T 759 **/ 760 auto callbackConfigurer(T, Args...)(Locator!() locator, void delegate(Locator!(), T, Args) dg, auto ref Args args) { 761 auto constr = new CallbackConfigurer!(T, void delegate(Locator!(), T, Args), Args)(dg, args); 762 constr.locator = locator; 763 return constr; 764 } 765 766 /** 767 ditto 768 **/ 769 auto callbackConfigurer(T, Args...)(Locator!() locator, void function(Locator!(), T, Args) dg, auto ref Args args) { 770 auto constr = new CallbackConfigurer!(T, void function(Locator!(), T, Args), Args)(dg, args); 771 constr.locator = locator; 772 return constr; 773 } 774 775 /** 776 ditto 777 **/ 778 auto callbackConfigurer(T, Args...)(Locator!() locator, void delegate(Locator!(), ref T, Args) dg, auto ref Args args) { 779 auto constr = new CallbackConfigurer!(T, void delegate(Locator!(), ref T, Args), Args)(dg, args); 780 constr.locator = locator; 781 return constr; 782 } 783 784 /** 785 ditto 786 **/ 787 auto callbackConfigurer(T, Args...)(Locator!() locator, void function(Locator!(), ref T, Args) dg, auto ref Args args) { 788 auto constr = new CallbackConfigurer!(T, void function(Locator!(), ref T, Args), Args)(dg, args); 789 constr.locator = locator; 790 return constr; 791 } 792 793 /** 794 Instantiates data of type T using another factory, and configures it with help of it's own property configurers. 795 **/ 796 class ParentAwareGenericFactory(T) : GenericFactory!T { 797 798 private { 799 Factory!T fact_; 800 Locator!() locator_; 801 PropertyConfigurer!T[] configurers; 802 } 803 804 public { 805 @property { 806 ParentAwareGenericFactory fact(Factory!T fact) @safe nothrow { 807 this.fact_ = fact; 808 809 return this; 810 } 811 812 Factory!T fact() @safe nothrow { 813 return this.fact_; 814 } 815 816 ParentAwareGenericFactory locator(Locator!() locator) @safe nothrow { 817 this.locator_ = locator; 818 819 return this; 820 } 821 822 Locator!() locator() @safe nothrow { 823 return this.locator_; 824 } 825 826 TypeInfo type() @safe nothrow { 827 return typeid(T); 828 } 829 } 830 831 T factory() { 832 T result = this.fact.factory; 833 834 foreach (configurer; this.configurers) { 835 configurer.configure(result); 836 } 837 838 return result; 839 } 840 841 ParentAwareGenericFactory!T setInstanceFactory(InstanceFactory!T factory) { 842 throw new AediException("Setting constructor for an object with parent is not supported yet."); 843 } 844 845 ParentAwareGenericFactory!T addPropertyConfigurer(PropertyConfigurer!T configurer) { 846 this.configurers ~= configurer; 847 848 return this; 849 } 850 } 851 } 852 853 GenericFactory!T genericFactory(T)(Locator!() locator) { 854 return new GenericFactoryImpl!T(locator); 855 } 856 857 /** 858 An check if the argument list passed to ConstructorBasedFactory or MethodConfigurer is compatible with signature of underlying 859 method or constructor. 860 861 Note: 862 For now it checks if the lengths are equal. For future it should also check if types are compatible. 863 **/ 864 template isArgumentListCompatible(alias func, ArgTuple...) 865 if (isSomeFunction!func) { 866 bool isArgumentListCompatible() { 867 alias FuncParams = Parameters!func; 868 alias Required = Filter!(partialSuffixed!(isValueOfType, void), ParameterDefaults!func); 869 870 static if ((ArgTuple.length < Required.length) || (ArgTuple.length > FuncParams.length)) { 871 872 return false; 873 } else { 874 875 bool result = true; 876 foreach (index, Argument; ArgTuple) { 877 878 static if (!is(Argument : LocatorReference) && !isImplicitlyConvertible!(Argument, FuncParams[index])) { 879 880 result = false; 881 break; 882 } 883 } 884 885 return result; 886 } 887 } 888 } 889 890 mixin template assertFieldCompatible(T, string field, Arg) { 891 import aermicioi.util.traits; 892 import std.traits; 893 import std.meta; 894 895 static assert(isField!(T, field), name!T ~ "'s " ~ field ~ " member is not a field"); 896 static assert(isProtection!(T, field, "public"), name!T ~ "'s " ~ field ~ " is not public and therefore cannot be accessed."); 897 static assert(is(Arg : LocatorReference) ? 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); 898 } 899 900 enum bool isFieldCompatible(T, string field, Arg) = 901 isField!(T, field) && 902 isProtection!(T, field, "public") && 903 is(Arg : LocatorReference) ? true : isImplicitlyConvertible!(Arg, typeof(getMember!(T, field))); 904 905 mixin template assertObjectConstructorCompatible(T, Args...) { 906 import aermicioi.util.traits; 907 import std.traits; 908 import std.meta; 909 910 static assert(hasMember!(T, "__ctor"), name!T ~ " doesn't have any constructor to call."); 911 static assert(isProtection!(T, "__ctor", "public"), name!T ~ "'s constructor is not public."); 912 static assert(isSomeFunction!(__traits(getMember, T, "__ctor")), name!T ~ "'s constructor is not a function, probably a template."); 913 static assert(variadicFunctionStyle!(__traits(getMember, T, "__ctor")) == Variadic.no, name!T ~ "'s constructor is a variadic function. Only non-variadic constructors are supported."); 914 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."); 915 } 916 917 enum bool isObjectConstructorCompatible(T, Args...) = isMethodCompatible!(T, "__ctor", Args); 918 919 mixin template assertObjectMethodCompatible(T, string method, Args...) { 920 import std.range : only; 921 import std.array : array; 922 import aermicioi.util.traits; 923 import std.traits; 924 import std.meta; 925 926 static assert(hasMember!(T, method), name!T ~ "'s method " ~ method ~ " not found."); 927 static assert(isProtection!(T, method, "public"), name!T ~ "'s method " ~ method ~ " is not public"); 928 static assert(isSomeFunction!(__traits(getMember, T, method)), name!T ~ "'s member " ~ method ~ " is not a function, probably a field, or a template."); 929 static assert(variadicFunctionStyle!(__traits(getMember, T, method)) == Variadic.no, name!T ~ "'s method " ~ method ~ "is variadic function. Only non-variadic methods are supported."); 930 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."); 931 } 932 933 enum bool isObjectMethodCompatible(T, string method, Args...) = isMethodCompatible!(T, method, Args); 934 935 936 template isMethodCompatible(T, string method, Args...) { 937 enum bool isMethodCompatible = 938 hasMember!(T, method) && 939 isProtection!(T, method, "public") && 940 isSomeFunction!(__traits(getMember, T, method)) && 941 (variadicFunctionStyle!(__traits(getMember, T, method)) == Variadic.no) && 942 (Filter!(partialSuffixed!(isArgumentListCompatible, Args), getOverloads!(T, method)).length == 1); 943 } 944 945 template getCompatibleOverload(T, string method, Args...) 946 if (isObjectMethodCompatible!(T, method, Args)) { 947 948 alias getCompatibleOverload = Filter!(partialSuffixed!(isArgumentListCompatible, Args), getOverloads!(T, method))[0]; 949 } 950 951 alias isStaticMethodCompatible = templateAnd!( 952 isMethodCompatible, 953 chain!( 954 isStaticFunction, 955 getCompatibleOverload 956 ) 957 ); 958 959 alias isNonStaticMethodCompatible = templateAnd!( 960 isMethodCompatible, 961 chain!( 962 templateNot!isStaticFunction, 963 getCompatibleOverload 964 ) 965 ); 966 967 alias isNonStaticMethodCompatibleAndReturnTypeOf(X) = templateAnd!( 968 isMethodCompatible, 969 chain!( 970 templateNot!isStaticFunction, 971 getCompatibleOverload 972 ), 973 partialSuffixed!( 974 chain!( 975 partialPrefixed!( 976 get, 977 0 978 ), 979 getCompatibleOverload 980 ), 981 X 982 ) 983 ); 984 985 private { 986 template isValueOfType(alias value, Type) { 987 enum bool isValueOfType = is(typeof(value) == Type); 988 } 989 990 template isValueOfType(Value, Type) { 991 enum bool isValueOfType = is(Value == Type); 992 } 993 994 enum bool isStruct(T) = is(T == struct); 995 996 alias hasDefaultCtor = 997 partialSuffixed!( 998 templateOr!( 999 templateNot!hasMember, 1000 chain!( 1001 isStruct, 1002 get!0 1003 ), 1004 templateAnd!( 1005 partialSuffixed!( 1006 isProtection, 1007 "public" 1008 ), 1009 chain!( 1010 partialPrefixed!( 1011 anySatisfy, 1012 eq!0 1013 ), 1014 partialPrefixed!( 1015 staticMap, 1016 arity 1017 ), 1018 chain!( 1019 partialPrefixed!( 1020 Filter, 1021 partialSuffixed!( 1022 isProtection, 1023 "public" 1024 ) 1025 ), 1026 getOverloads 1027 ) 1028 ) 1029 ) 1030 ), 1031 "__ctor" 1032 ); 1033 }