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.allocator_aware; 40 import aermicioi.aedi.storage.locator_aware; 41 import aermicioi.aedi.storage.wrapper; 42 import aermicioi.aedi.util.traits; 43 import aermicioi.aedi.util.formatting : separated, wrapped; 44 import aermicioi.aedi.util.typecons : Subscribable; 45 46 47 import std.conv : to; 48 import std.experimental.logger; 49 import std.meta; 50 import std.traits; 51 52 /** 53 A property configurer, has the purpose to modify component of type T according to some logic encapsulated in it. 54 55 **/ 56 @safe interface PropertyConfigurer(T) : LocatorAware!() { 57 58 public { 59 60 /** 61 Accepts a reference to an object that is to be configured by the configurer. 62 63 Params: 64 object = An object of type T, that will be configured 65 **/ 66 void configure(ref T object) @safe; 67 } 68 } 69 70 /** 71 A default implementation that delegates to decorated configurator. 72 **/ 73 mixin template PropertyConfigurerDecoratorMixin(T) { 74 public { 75 76 /** 77 Accepts a reference to an object that is to be configured by the configurer. 78 79 Params: 80 object = An object of type T, that will be configured 81 **/ 82 void configure(ref T object) @safe { 83 this.decorated.configure(object); 84 } 85 } 86 } 87 88 /** 89 An instance factory, instantiates component of type T. 90 **/ 91 @safe interface InstanceFactory(T) : LocatorAware!(), AllocatorAware!() { 92 93 public { 94 95 /** 96 Create a new instance of object of type T. 97 98 Returns: 99 T instantiated component 100 **/ 101 T factory() @safe; 102 } 103 } 104 105 /** 106 A default implementation that delegates construction to decorated implementation 107 **/ 108 mixin template InstanceFactoryDecoratorMixin(T) { 109 public { 110 111 /** 112 Create a new instance of object of type T. 113 114 Returns: 115 T instantiated component 116 **/ 117 T factory() @safe { 118 return this.decorated.factory(); 119 } 120 } 121 } 122 123 /** 124 An interface for components that can destruct components of type T and deallocate them using provided allocator. 125 **/ 126 @safe interface InstanceDestructor(T) : LocatorAware!(), AllocatorAware!() { 127 128 public { 129 130 /** 131 Destruct a component of type T and deallocate it using stored allocator. 132 133 Params: 134 destructable = element to be destructed and deallocated using stored allocator 135 **/ 136 void destruct(ref T destructable) @safe; 137 } 138 } 139 140 /** 141 A default implementation that delegates the destrcution to decorated component. 142 **/ 143 mixin template InstanceDestructorDecoratorMixin(T) { 144 public { 145 146 /** 147 Destruct a component of type T and deallocate it using stored allocator. 148 149 Params: 150 destructable = element to be destructed and deallocated using stored allocator 151 **/ 152 void destruct(ref T destructable) @safe { 153 return this.decorated.destruct(destructable); 154 } 155 } 156 } 157 158 /** 159 Interface for objects that are aware of an instance factory 160 and can use it to instantiate a component. 161 **/ 162 @safe interface InstanceFactoryAware(T) { 163 public { 164 165 @property { 166 /** 167 Sets the constructor of new object. 168 169 Params: 170 factory = a factory of objects of type T. 171 172 Returns: 173 The InstanceFactoryAware 174 **/ 175 InstanceFactoryAware!T setInstanceFactory(InstanceFactory!T factory) @safe; 176 } 177 } 178 } 179 180 /** 181 A default implementation that delegates the instance factory setting to decorated component. 182 **/ 183 mixin template InstanceFactoryAwareDecoratorMixin(T) { 184 public { 185 186 /** 187 Sets the constructor of new object. 188 189 Params: 190 factory = a factory of objects of type T. 191 192 Returns: 193 The InstanceFactoryAware 194 **/ 195 InstanceFactoryAware!T setInstanceFactory(InstanceFactory!T factory) @safe 196 in (factory !is null, "Expected a instance factory to be passed not null.") { 197 return this.decorated.setInstanceFactory(factory); 198 } 199 } 200 } 201 202 /** 203 A default implementation for storing/accessing instance factory. 204 **/ 205 mixin template InstanceFactoryAwareMixin(T) { 206 private { 207 InstanceFactory!T instanceFactory; 208 } 209 210 public { 211 212 /** 213 Sets the constructor of new object. 214 215 Params: 216 factory = a factory of objects of type T. 217 218 Returns: 219 The InstanceFactoryAware 220 **/ 221 typeof(this) setInstanceFactory(InstanceFactory!T factory) @safe 222 in (factory !is null, "Expected a instance factory, got a null reference.") { 223 instanceFactory = factory; 224 return this; 225 } 226 } 227 } 228 229 /** 230 Interface for objects that are aware of an instance factory 231 and can use it to instantiate a component. 232 **/ 233 @safe interface InstanceDestructorAware(T) { 234 public @property { 235 236 /** 237 Sets the destructor of component. 238 239 Params: 240 destructor = destructor for components of type T. 241 242 Returns: 243 The InstanceDestructorAware!T 244 **/ 245 InstanceDestructorAware!T setInstanceDestructor(InstanceDestructor!T destructor) @safe; 246 } 247 } 248 249 /** 250 A default implementation that delegates the instance destructor setting to decorated component. 251 **/ 252 mixin template InstanceDestructorAwareDecoratorMixin(T) { 253 public { 254 255 /** 256 Sets the destructor of component. 257 258 Params: 259 destructor = destructor for components of type T. 260 261 Returns: 262 The InstanceDestructorAware!T 263 **/ 264 InstanceDestructorAware!T setInstanceDestructor(InstanceDestructor!T destructor) @safe 265 in (destructor !is null, "Expected an instance destructor to passed not null.") { 266 return this.decorated.setInstanceDestructor(destructor); 267 } 268 } 269 } 270 271 /** 272 A default implementation for storing/accessing instance destructor. 273 **/ 274 mixin template InstanceDestructorAwareMixin(T) { 275 private { 276 InstanceDestructor!T instanceDestructor; 277 } 278 279 public { 280 281 /** 282 Sets the destructor of component. 283 284 Params: 285 destructor = destructor for components of type T. 286 287 Returns: 288 The InstanceDestructorAware!T 289 **/ 290 typeof(this) setInstanceDestructor(InstanceDestructor!T destructor) @safe 291 in (destructor !is null, "Expected a instance destructor, got a null reference.") { 292 instanceDestructor = destructor; 293 294 return this; 295 } 296 } 297 } 298 299 /** 300 Interface for objects that are aware of a set of property configurers 301 and can use them to configure some component. 302 **/ 303 @safe interface PropertyConfigurersAware(T) { 304 public { 305 /** 306 Adds an configurer to the PropertyConfigurersAware. 307 308 Params: 309 configurer = a configurer that will be invoked after factory of an object. 310 311 Returns: 312 The PropertyConfigurersAware instance 313 **/ 314 PropertyConfigurersAware!T addPropertyConfigurer(PropertyConfigurer!T configurer) @safe; 315 } 316 } 317 318 /** 319 A default implementation that delegates the instance destructor setting to decorated component. 320 **/ 321 mixin template PropertyConfigurersAwareDecoratorMixin(T) { 322 public { 323 324 /** 325 Adds an configurer to the PropertyConfigurersAware. 326 327 Params: 328 configurer = a configurer that will be invoked after factory of an object. 329 330 Returns: 331 The PropertyConfigurersAware instance 332 **/ 333 PropertyConfigurersAware!T addPropertyConfigurer(PropertyConfigurer!T configurer) @safe { 334 return this.decorated.addPropertyConfigurer(configurer); 335 } 336 } 337 } 338 339 /** 340 A default implementation for storing property configurers. 341 **/ 342 mixin template PropertyConfigurersAwareMixin(T) { 343 private { 344 PropertyConfigurer!T[] propertyConfigurers; 345 } 346 public { 347 348 /** 349 Adds an configurer to the PropertyConfigurersAware. 350 351 Params: 352 configurer = a configurer that will be invoked after factory of an object. 353 354 Returns: 355 The PropertyConfigurersAware instance 356 **/ 357 typeof(this) addPropertyConfigurer(PropertyConfigurer!T configurer) @safe 358 in (configurer !is null, "Expected a instance configurer, got a null reference.") { 359 this.propertyConfigurers ~= configurer; 360 361 return this; 362 } 363 } 364 } 365 366 /** 367 A generic factory, is a factory that instantiates component of type T using InstanceFactory and a list of PropertyConfigurers. 368 369 A generic factory, is a factory that instantiates component of type T using InstanceFactory and a list of PropertyConfigurers. 370 It can optionally provide a Locator!() object to InstanceFactory and PropertyConfigurer to be used as a source of component. 371 **/ 372 @safe interface GenericFactory(T) : Factory!T, InstanceFactoryAware!T, PropertyConfigurersAware!T, InstanceDestructorAware!T { 373 374 public { 375 376 @property { 377 378 alias locator = Factory!T.locator; 379 380 /** 381 Get the GenericFactory locator. 382 383 Returns: 384 Locator!() the locator that should be used by underlying constructor or property configurer. 385 **/ 386 inout(Locator!()) locator() @safe inout; 387 } 388 } 389 } 390 391 /** 392 A concrete implementation of GenericFactory interface. 393 **/ 394 @safe class GenericFactoryImpl(T) : GenericFactory!T, LocatorAware!() { 395 import aermicioi.aedi.storage.allocator_aware : AllocatorAwareMixin; 396 397 private { 398 /** 399 Somehow scoped methods got leaked into overload set of generic factory messing up additional logic. 400 **/ 401 struct Payload { 402 mixin LocatorAwareMixin!() DefaultLocatorImplementation; 403 mixin AllocatorAwareMixin!RCIAllocator DefaultAllocatorImplementation; 404 mixin InstanceFactoryAwareMixin!T DefaultInstanceFactoryAwareImplementation; 405 mixin InstanceDestructorAwareMixin!T DefaultInstanceDestructorAwareImplementation; 406 mixin PropertyConfigurersAwareMixin!T DefaultPropertyConfigurersAwareImplementation; 407 } 408 409 Payload payload; 410 alias payload this; 411 } 412 413 414 public { 415 416 /** 417 Constructor for GenericFactoryImpl 418 419 Params: 420 locator = the locator used by constructor to fetch dependencies of the created object 421 **/ 422 this(Locator!() locator) { 423 import std.experimental.allocator : theAllocator; 424 425 DefaultLocatorImplementation.locator = locator; 426 DefaultAllocatorImplementation.allocator = theAllocator; 427 428 static if (hasDefaultCtor!T) { 429 this.setInstanceFactory(new DefaultInstanceFactory!T); 430 } else { 431 this.setInstanceFactory(new DefaultFailingInstanceFactory!T); 432 } 433 434 this.setInstanceDestructor(new DefaultInstanceDestructor!T); 435 } 436 437 /** 438 Create a new instance of object of type T using provided instance factory and property configurers. 439 440 Returns: 441 T instantiated component 442 **/ 443 T factory() @safe { 444 debug(trace) trace("Instantiating component of type ", typeid(T)); 445 T instance = this.instanceFactory.factory(); 446 447 foreach (configurer; this.propertyConfigurers) { 448 449 debug(trace) trace("Running ", configurer, " configurer over instantiated component."); 450 configurer.configure(instance); 451 } 452 453 return instance; 454 } 455 456 /** 457 Destructs a component of type T. 458 459 Params: 460 component = component that is to ve destroyed. 461 **/ 462 void destruct(ref T component) @safe { 463 debug(trace) trace("Destroying component of type ", typeid(T)); 464 this.instanceDestructor.destruct(component); 465 } 466 467 @property { 468 469 /** 470 Get the type info of object that is created. 471 472 Returns: 473 TypeInfo object of created object. 474 **/ 475 TypeInfo type() @safe nothrow const { 476 return typeid(T); 477 } 478 } 479 480 /** 481 Set destructor 482 483 Params: 484 destructor = the destructor used to destruct components created by this factory. 485 486 Returns: 487 typeof(this) 488 **/ 489 typeof(this) setInstanceDestructor(InstanceDestructor!T destructor) @safe 490 in (destructor !is null, "Expected a destructor for component " ~ typeid(T).toString ~ " to be passed not null.") { 491 DefaultInstanceDestructorAwareImplementation.setInstanceDestructor(destructor); 492 493 this.instanceDestructor.allocator = DefaultAllocatorImplementation.allocator; 494 this.instanceDestructor.locator = DefaultLocatorImplementation.locator; 495 496 return this; 497 } 498 499 /** 500 Sets the constructor of new object. 501 502 Params: 503 factory = a factory of objects of type T. 504 505 Returns: 506 The InstanceFactoryAware 507 **/ 508 GenericFactory!T setInstanceFactory(InstanceFactory!T factory) @safe 509 in (factory !is null, "Expected for instance factory to be passed for component " ~ typeid(T).toString ~ " not null.") { 510 DefaultInstanceFactoryAwareImplementation.setInstanceFactory(factory); 511 512 this.instanceFactory.allocator = DefaultAllocatorImplementation.allocator; 513 this.instanceFactory.locator = DefaultLocatorImplementation.locator; 514 515 return this; 516 } 517 518 /** 519 Set locator 520 521 Params: 522 locator = the locator used to fetch created's object dependencies 523 Returns: 524 typeof(this) 525 **/ 526 GenericFactory!T locator(Locator!() locator) @safe nothrow { 527 DefaultLocatorImplementation.locator(locator); 528 529 this.instanceFactory.locator = DefaultLocatorImplementation.locator; 530 this.instanceDestructor.locator = DefaultLocatorImplementation.locator; 531 532 foreach (configurer; this.propertyConfigurers) { 533 configurer.locator = locator; 534 } 535 536 return this; 537 } 538 539 /** 540 Get locator 541 542 Returns: 543 Locator!() 544 **/ 545 inout(Locator!()) locator() @safe nothrow inout { 546 return DefaultLocatorImplementation.locator; 547 } 548 549 /** 550 Set allocator 551 552 Params: 553 allocator = the allocator used to allocate place for component. 554 555 Returns: 556 typeof(this) 557 **/ 558 typeof(this) allocator(RCIAllocator allocator) @safe nothrow { 559 DefaultAllocatorImplementation.allocator(allocator); 560 561 this.instanceFactory.allocator = DefaultAllocatorImplementation.allocator; 562 this.instanceDestructor.allocator = DefaultAllocatorImplementation.allocator; 563 564 return this; 565 } 566 567 /** 568 Get allocator 569 570 Returns: 571 RCIAllocator 572 **/ 573 inout(RCIAllocator) allocator() @safe nothrow inout { 574 return DefaultAllocatorImplementation.allocator; 575 } 576 577 /** 578 Adds an configurer to the PropertyConfigurersAware. 579 580 Params: 581 configurer = a configurer that will be invoked after factory of an object. 582 583 Returns: 584 The PropertyConfigurersAware instance 585 **/ 586 GenericFactory!T addPropertyConfigurer(PropertyConfigurer!T configurer) @safe { 587 DefaultPropertyConfigurersAwareImplementation.addPropertyConfigurer(configurer); 588 configurer.locator = DefaultLocatorImplementation.locator; 589 590 return this; 591 } 592 } 593 594 } 595 596 /** 597 ditto 598 **/ 599 GenericFactory!T genericFactory(T)(Locator!() locator) { 600 return new GenericFactoryImpl!T(locator); 601 } 602 603 /** 604 ParameterHolder Stores a set of Args for futher usage in it's subclasses. 605 606 Params: 607 Args = a type tuple of args that ParameterHolder can hold. 608 **/ 609 mixin template ParameterHolder(Args...) { 610 611 protected { 612 Args args; 613 } 614 } 615 616 /** 617 Calls component's method with a set of arguments. 618 619 Encapsulates a call to component's method, with a set of arguments. 620 The algorithm that calls component's method, will automatically replace 621 RuntimeReferences from args list with components extracted from container, casted to 622 type that is extracted from method's signature. 623 624 Params: 625 T = the component type 626 property = method that will be called 627 Args = type tuple of args that method can be called with. 628 **/ 629 @safe class MethodConfigurer(T, string property, Args...) : PropertyConfigurer!T 630 if ( 631 isMethodCompatible!(T, property, Args) 632 ) { 633 634 mixin ParameterHolder!Args; 635 mixin LocatorAwareMixin!(typeof(this)); 636 637 public { 638 639 /** 640 Constructor for MethodConfigurer!(T, property, Args) 641 642 Params: 643 args = list of arguments passed to T's method 644 **/ 645 this(ref Args args) @safe { 646 this.args = args; 647 } 648 649 /** 650 See PropertyConfigurer interface 651 652 Throws: 653 InvalidCastException when extracted component by reference, is not of type expected by argument 654 of component's method 655 **/ 656 void configure(ref T obj) @trusted { 657 658 try { 659 static foreach (overload; __traits(getOverloads, T, property)) { 660 static if (!is(typeof(called)) && isArgumentListCompatible!(overload, Args)) { 661 enum called = true; 662 alias ArgTuple = Parameters!overload; 663 debug(trace) trace("Calling method ", property, " over ", typeid(T), " with arguments of ", ", ".separated(this.args).wrapped); 664 mixin(q{__traits(getMember, obj, property)(} ~ compileArgumentsTuple!ArgTuple(q{ArgTuple}, q{this.args}, q{this.locator}) ~ q{);}); 665 } 666 } 667 } catch (Exception e) { 668 throw new PropertyConfigurerException("Error occurred while invoking method ${type}.${property} of ${identity} component ", null, property, typeid(T), e); 669 } 670 } 671 } 672 } 673 674 /** 675 ditto 676 **/ 677 auto methodConfigurer(string property, T, Args...)(auto ref Args args) 678 if (!isField!(T, property)) { 679 mixin assertObjectMethodCompatible!(T, property, Args); 680 681 auto propertySetter = new MethodConfigurer!(T, property, Args)(args); 682 683 return propertySetter; 684 } 685 686 /** 687 Sets component's field to a value. 688 689 Encapsulates logic that sets component's field to a certain value. 690 If argument that is contained by configurer is a RuntimeReference, it will be automatically 691 replaced with value extracted from locator, and set to component's field. 692 **/ 693 @safe class FieldConfigurer(T, string property, Arg) : PropertyConfigurer!T 694 if ( 695 isFieldCompatible!(T, property, Arg) 696 ) { 697 698 mixin ParameterHolder!Arg; 699 mixin LocatorAwareMixin!(typeof(this)); 700 701 public { 702 /** 703 Constructor for MethodConfigurer!(T, property, Args) 704 705 Params: 706 arg = list of arguments passed to T's method 707 **/ 708 this(ref Arg arg) @safe { 709 this.args[0] = arg; 710 } 711 712 /** 713 See PropertyConfigurer interface 714 715 Throws: 716 InvalidCastException when extracted component by reference, is not of type expected by argument 717 of component's field 718 **/ 719 void configure(ref T obj) @trusted { 720 721 try { 722 723 debug(trace) trace("Assigning to field ", property, " of ", typeid(T), " value of ", this.args[0]); 724 __traits(getMember, obj, property) = args[0].resolve!( 725 typeof(__traits(getMember, obj, property)) 726 )( 727 this.locator 728 ); 729 } catch (Exception e) { 730 731 throw new PropertyConfigurerException("Error occurred while assigning a value to ${type}.${property} of ${identity} component", null, property, typeid(T), e); 732 } 733 } 734 } 735 } 736 737 /** 738 ditto 739 **/ 740 auto fieldConfigurer(string property, T, Arg)(auto ref Arg arg) 741 if (isField!(T, property)) { 742 mixin assertFieldCompatible!(T, property, Arg); 743 744 auto propertySetter = new FieldConfigurer!(T, property, Arg)(arg); 745 746 return propertySetter; 747 } 748 749 /** 750 Instantiates a component using it's constructor with no arguments. 751 **/ 752 @safe class DefaultInstanceFactory(T) : InstanceFactory!T 753 if ( 754 hasDefaultCtor!T 755 ) { 756 757 mixin LocatorAwareMixin!(typeof(this)); 758 mixin AllocatorAwareMixin!(typeof(this)); 759 760 public { 761 /** 762 Create a new instance of object of type T. 763 764 Returns: 765 T instantiated component 766 **/ 767 T factory() @trusted { 768 769 try { 770 debug(trace) trace("Instantiating ", typeid(T), " using default constructor or value."); 771 772 static if (is(T == class) && !isAbstractClass!T) { 773 return this.allocator.make!T(); 774 } else { 775 return T.init; 776 } 777 } catch (Exception e) { 778 throw new InstanceFactoryException("Error occurred while default constructor was run for ${identity} of ${type} type", null, typeid(T), e); 779 } 780 } 781 } 782 } 783 784 /** 785 An instance constructor aimed to throw exception. 786 787 An instance constructor aimed to throw exception, 788 when a component has elaborate constructor, yet 789 no constructor was configured for generic factory to 790 use those elaborate constructors. 791 **/ 792 @safe class DefaultFailingInstanceFactory(T) : InstanceFactory!T { 793 794 mixin LocatorAwareMixin!(typeof(this)); 795 mixin AllocatorAwareMixin!(typeof(this)); 796 797 public { 798 /** 799 Create a new instance of object of type T. 800 801 Returns: 802 T instantiated component 803 **/ 804 T factory() @safe { 805 806 throw new InstanceFactoryException("Component ${identity} of ${type} has elaborate constructor, yet it wasn't configured to run it.", null, typeid(T)); 807 } 808 } 809 } 810 811 /** 812 Instantiates component using it's constructor with args. 813 814 Encapsulates construction of component using a constructor, with args. 815 Arguments from argument list that are RuntimeReferences, are automatically 816 replaced with component extracted from locator. 817 818 Params: 819 T = component type 820 Args = type tuple of args that are passed to T's constructor 821 **/ 822 @safe class ConstructorBasedFactory(T, Args...) : InstanceFactory!T 823 if ( 824 isObjectConstructorCompatible!(T, Args) 825 ) { 826 827 mixin ParameterHolder!Args; 828 mixin AllocatorAwareMixin!(typeof(this)); 829 mixin LocatorAwareMixin!(typeof(this)); 830 831 public { 832 /** 833 Constructor for ConstructorBasedFactory!(T, Args) 834 835 Params: 836 args = arguments used for constructor 837 **/ 838 this(ref Args args) @safe { 839 this.args = args; 840 } 841 842 /** 843 See InstanceFactory interface 844 845 Throws: 846 InvalidCastException when extracted component by reference, is not of type expected by argument 847 of component's constructor 848 **/ 849 T factory() @trusted { 850 851 try { 852 853 static foreach (overload; __traits(getOverloads, T, "__ctor")) { 854 static if (!is(typeof(called)) && isArgumentListCompatible!(overload, Args)) { 855 enum called = true; 856 alias ConstructorArgs = Parameters!overload; 857 debug(trace) trace("Instantiating ", typeid(T), " using constructor with arguments of ", ", ".separated(this.args).wrapped); 858 static if (is(T : Object)) { 859 mixin(q{return this.allocator.make!T(} ~ compileArgumentsTuple!ConstructorArgs(q{ConstructorArgs}, q{this.args}, q{this.locator}) ~ q{);}); 860 } else { 861 mixin(q{return T(} ~ compileArgumentsTuple!ConstructorArgs(q{ConstructorArgs}, q{this.args}, q{this.locator}) ~ q{);}); 862 } 863 } 864 } 865 } catch (Exception e) { 866 867 throw new InstanceFactoryException("Error occurred while elaborate constructor was run on ${identity} of ${type} type", null, typeid(T), e); 868 } 869 } 870 } 871 } 872 873 /** 874 ditto 875 **/ 876 auto constructorBasedFactory(T, Args...)(auto ref Args args) { 877 mixin assertObjectConstructorCompatible!(T, Args); 878 auto constructor = new ConstructorBasedFactory!(T, Args)(args); 879 880 return constructor; 881 } 882 883 /** 884 Instantiates a component using a function/delegate with args 885 886 Encapsulates construction of component using a function, with args. 887 Arguments from argument list that are RuntimeReferences, are automatically 888 replaced with component extracted from locator. 889 890 Params: 891 Dg = type of function or delegate 892 Args = arguments mixed with references used to resolve function arguments 893 **/ 894 template FunctionInstanceFactory(Dg, Args...) 895 if (isSomeFunction!Dg) { 896 897 alias Z = ReturnType!Dg; 898 alias Params = Parameters!Dg; 899 900 @safe class FunctionInstanceFactory : InstanceFactory!Z { 901 mixin AllocatorAwareMixin!(typeof(this)); 902 mixin LocatorAwareMixin!(typeof(this)); 903 mixin ParameterHolder!Args; 904 905 private { 906 Dg dg; 907 } 908 909 this(Dg dg, Args args) 910 in (dg !is null, "Cannot have a null function factory, required " ~ typeid(Dg).toString ~ " function.") { 911 this.dg = dg; 912 this.args = args; 913 } 914 915 /** 916 Create a new instance of object of type T. 917 918 Returns: 919 T instantiated component 920 **/ 921 Z factory() @trusted { 922 try { 923 debug(trace) trace("Instantiating ", typeid(Z), " using function/delegate ", typeid(Dg), " with arguments of ", ", ".separated(this.args).wrapped); 924 mixin( 925 q{return dg(} ~ 926 compileArgumentsTuple!Params(q{Params}, q{this.args}, q{this.locator}) ~ 927 q{);} 928 ); 929 } catch (Exception e) { 930 import std.conv : text; 931 throw new InstanceFactoryException( 932 text( 933 "Error occurred during construction of ${type} using function/delegate ", 934 typeid(Dg).toString 935 ), 936 null, 937 typeid(Z), 938 e 939 ); 940 } 941 } 942 } 943 } 944 945 /** 946 ditto 947 **/ 948 auto functionInstanceFactory(Dg, Args...)(Dg dg, Args args) { 949 return new FunctionInstanceFactory!(Dg, Args)(dg, args); 950 } 951 952 /** 953 Instantiates a component using a method from other component (factory method pattern). 954 955 Encapsulates construction of component using factory method. 956 Arguments that are RuntimeReferences, will be replaced with components extracted 957 from locator, and passed to factory's method. 958 In case when method is not static member, the algorithm will use 959 an instantiaton of factory passed to it, or extracted from locator 960 if a RuntimeReference is passed. 961 962 Params: 963 T = factory that is used to instantiate component using it's method 964 method = the name of method used to instantiate component 965 W = the factory T, or a RuntimeReference to the factory. 966 Args = type tuple of arguments passed to factory. 967 **/ 968 969 template FactoryMethodBasedFactory(T, string method, W, Args...) { 970 alias FactoryMethodBasedFactory = FactoryMethodInstanceFactory!(method, T, W, Args); 971 } 972 973 /** 974 ditto 975 **/ 976 template FactoryMethodInstanceFactory(string method, T, W, Args...) 977 if ( 978 isMethodCompatible!(T, method, Args) && 979 isAggregateType!(ReturnType!(getCompatibleOverload!(T, method, Args))) && 980 (is(W : RuntimeReference) || is(W : T)) 981 ) { 982 983 alias Compatible = getCompatibleOverload!(T, method, Args); 984 alias Z = ReturnType!Compatible; 985 alias Params = Parameters!Compatible; 986 987 @safe class FactoryMethodInstanceFactory : InstanceFactory!Z { 988 989 mixin AllocatorAwareMixin!(typeof(this)); 990 mixin LocatorAwareMixin!(typeof(this)); 991 mixin ParameterHolder!Args; 992 W componentFactory; 993 994 this(W componentFactory, Args args) 995 in ((is(typeof(componentFactory is null) : bool) && (componentFactory !is W.init)) 996 || !is(typeof(componentFactory is null) : bool), 997 "Cannot instantiate a component using a factory method when factory itself is not provided. Expected " ~ typeid(T).toString) { 998 999 this.componentFactory = componentFactory; 1000 this.args = args; 1001 } 1002 1003 /** 1004 Create a new instance of object of type T. 1005 1006 Returns: 1007 T instantiated component 1008 **/ 1009 Z factory() @trusted { 1010 try { 1011 debug(trace) trace("Instantiating ", typeid(Z), " using factory method ", method, " of ", typeid(T), " with arguments of ", ", ".separated(this.args).wrapped); 1012 mixin( 1013 q{return __traits(getMember, this.componentFactory.resolve!T(this.locator), method)(} ~ 1014 compileArgumentsTuple!Params(q{Params}, q{this.args}, q{this.locator}) ~ 1015 q{);} 1016 ); 1017 } catch (Exception e) { 1018 import std.conv : text; 1019 throw new InstanceFactoryException( 1020 text( 1021 "Error occurred during construction of ${type} using factory method of ", 1022 name!T, 1023 ".", 1024 method 1025 ), 1026 null, 1027 typeid(Z), 1028 e 1029 ); 1030 } 1031 } 1032 } 1033 } 1034 1035 /** 1036 ditto 1037 **/ 1038 auto factoryMethodBasedFactory 1039 (T, string method, Args...) 1040 (Args args) 1041 { 1042 static if ( 1043 (Args.length > 0) && 1044 ( 1045 is(Args[0] : RuntimeReference) || 1046 is(Args[0] : T) 1047 ) && 1048 isNonStaticMethodCompatible!(T, method, Args[1 .. $]) 1049 ) { 1050 1051 return new FactoryMethodInstanceFactory!(method, T, Args)(args); 1052 } else static if ( 1053 isStaticMethodCompatible!(T, method, Args) 1054 ) { 1055 1056 alias overload = getCompatibleOverload!(T, method, Args); 1057 return functionInstanceFactory(&overload, args); 1058 } else { 1059 1060 return null; 1061 } 1062 } 1063 1064 1065 /** 1066 Instantiates component of type T using a delegate or function. 1067 1068 Encapsulates component's construction logic using a delegate. 1069 The algorithm uses a delegate to create required component, 1070 with a set of Args that are passed to delegate, and a locator 1071 for dependency fetching. 1072 1073 Params: 1074 T = the constructed component 1075 Args = type tuple of arguments passed to delegate for component's construction. 1076 **/ 1077 @safe class CallbackFactory(T, Dg, Args...) : InstanceFactory!T 1078 if ((is(Dg == T delegate (RCIAllocator, Locator!(), Args)) || is(Dg == T function (RCIAllocator, Locator!(), Args)))) { 1079 1080 mixin AllocatorAwareMixin!(typeof(this)); 1081 mixin LocatorAwareMixin!(typeof(this)); 1082 mixin ParameterHolder!Args; 1083 1084 private { 1085 Dg dg; 1086 } 1087 1088 public { 1089 /** 1090 Constructor for CallbackFactory!(T, Dg, Args) 1091 1092 Params: 1093 dg = delegate used to create object 1094 args = arguments passed to delegate 1095 **/ 1096 this(Dg dg, ref Args args) @safe { 1097 this.dg = dg; 1098 this.args = args; 1099 } 1100 1101 /** 1102 See InstanceFactory interface 1103 **/ 1104 T factory() @trusted { 1105 try { 1106 debug(trace) trace("Instantiating ", typeid(T), " using function/delegate ", typeid(Dg), " with arguments of ", ", ".separated(this.args).wrapped); 1107 return this.dg(this.allocator, this.locator, args); 1108 } catch (Exception e) { 1109 1110 throw new InstanceFactoryException( 1111 "Error occurred during construction of ${identity} of ${type} type using callback factory", null, typeid(T), 1112 e 1113 ); 1114 } 1115 } 1116 } 1117 } 1118 1119 /** 1120 ditto 1121 **/ 1122 auto callbackFactory(T, Args...)(T delegate(RCIAllocator, Locator!(), Args) dg, auto ref Args args) { 1123 auto constr = new CallbackFactory!(T, T delegate(RCIAllocator, Locator!(), Args), Args)(dg, args); 1124 return constr; 1125 } 1126 1127 /** 1128 ditto 1129 **/ 1130 auto callbackFactory(T, Args...)(T function(RCIAllocator, Locator!(), Args) dg, auto ref Args args) { 1131 auto constr = new CallbackFactory!(T, T function(RCIAllocator, Locator!(), Args), Args)(dg, args); 1132 return constr; 1133 } 1134 1135 /** 1136 Configures/modifies component of type T with help of a delegate or function. 1137 1138 Encapsulates component configuration logic using a delegate. 1139 The algorithm calls delegate, with a locator, a set of Args, 1140 and configured component, in order to modify the component. 1141 1142 Note: 1143 If component is not a reference type it is recommended to pass it by ref 1144 in order to avoid receiving of a copy and not original one in delegate. 1145 It is expected that the callback will use somehow method on which it was annotated with. 1146 Params: 1147 T = the component 1148 Args = type tuple of arguments used by delegate for customization. 1149 **/ 1150 @safe class CallbackConfigurer(T, X, Dg, Args...) : PropertyConfigurer!T 1151 if ( 1152 is(T : X) && ( 1153 is(Dg : void delegate (Locator!(), X, Args)) || 1154 is(Dg : void function (Locator!(), X, Args)) || 1155 is(Dg : void delegate (Locator!(), ref X, Args)) || 1156 is(Dg : void function (Locator!(), ref X, Args)) 1157 ) 1158 ) { 1159 1160 mixin ParameterHolder!Args; 1161 mixin LocatorAwareMixin!(typeof(this)); 1162 1163 private { 1164 Dg dg; 1165 } 1166 1167 public { 1168 /** 1169 Constructor for CallbackConfigurer!(T, Dg, Args) 1170 1171 Params: 1172 dg = delegate used to configure the created object 1173 args = arguments passed to delegate 1174 **/ 1175 this(Dg dg, ref Args args) @safe { 1176 this.dg = dg; 1177 this.args = args; 1178 } 1179 1180 /** 1181 Accepts a reference to an object that is to be configured by the configurer. 1182 1183 Params: 1184 object = An object of type T, that will be configured 1185 **/ 1186 void configure(ref T object) @trusted { 1187 1188 try { 1189 debug(trace) trace("Configuring ", typeid(T), " using function/delegate ", typeid(Dg), " with arguments of ", ", ".separated(this.args).wrapped); 1190 return this.dg(this.locator, object, args); 1191 } catch (Exception e) { 1192 throw new PropertyConfigurerException("Error occurred while running a callback over ${identity} of ${type} component", null, null, typeid(T), e); 1193 } 1194 } 1195 } 1196 } 1197 1198 /** 1199 ditto 1200 **/ 1201 auto callbackConfigurer(T, X, Args...)(void delegate(Locator!(), X, Args) dg, auto ref Args args) if (is(T : X)) { 1202 auto constr = new CallbackConfigurer!(T, X, void delegate(Locator!(), X, Args), Args)(dg, args); 1203 return constr; 1204 } 1205 1206 /** 1207 ditto 1208 **/ 1209 auto callbackConfigurer(T, X, Args...)(void function(Locator!(), X, Args) dg, auto ref Args args) if (is(T : X)) { 1210 auto constr = new CallbackConfigurer!(T, X, void function(Locator!(), X, Args), Args)(dg, args); 1211 return constr; 1212 } 1213 1214 /** 1215 ditto 1216 **/ 1217 auto callbackConfigurer(T, X, Args...)(void delegate(Locator!(), ref X, Args) dg, auto ref Args args) if (is(T : X)) { 1218 auto constr = new CallbackConfigurer!(T, X, void delegate(Locator!(), ref X, Args), Args)(dg, args); 1219 return constr; 1220 } 1221 1222 /** 1223 ditto 1224 **/ 1225 auto callbackConfigurer(T, X, Args...)(void function(Locator!(), ref X, Args) dg, auto ref Args args) if (is(T : X)) { 1226 auto constr = new CallbackConfigurer!(T, X, void function(Locator!(), ref X, Args), Args)(dg, args); 1227 return constr; 1228 } 1229 1230 1231 /** 1232 Instantiates a component using a value as basis. 1233 1234 Instantiates a component using a value as basis. 1235 As a consequence, any reference based type will 1236 point to same content when it is instantiated 1237 multiple times. 1238 **/ 1239 @safe class ValueInstanceFactory(T) : InstanceFactory!T { 1240 private { 1241 T initial_; 1242 } 1243 1244 mixin AllocatorAwareMixin!(typeof(this)); 1245 mixin LocatorAwareMixin!(typeof(this)); 1246 1247 public { 1248 /** 1249 Constructor for ValueInstanceFactory!T 1250 1251 Params: 1252 initial = argument that is to be passed as created object 1253 **/ 1254 this(T initial) @safe { 1255 this.initial = initial; 1256 } 1257 1258 @property { 1259 1260 /** 1261 Set initial 1262 1263 Params: 1264 initial = value which will be passed as created component 1265 Returns: 1266 typeof(this) 1267 **/ 1268 ValueInstanceFactory!T initial(T initial) @safe nothrow { 1269 this.initial_ = initial; 1270 1271 return this; 1272 } 1273 1274 /** 1275 Get initial 1276 1277 Returns: 1278 T 1279 **/ 1280 T initial() @safe nothrow { 1281 return this.initial_; 1282 } 1283 } 1284 1285 /** 1286 Create a new instance of object of type T. 1287 1288 Returns: 1289 T instantiated component 1290 **/ 1291 T factory() @safe { 1292 debug(trace) trace("Providing existing ", typeid(T), " as instantiated component."); 1293 return this.initial(); 1294 } 1295 } 1296 } 1297 1298 /** 1299 InstanceFactory that delegates the task of instantiating a component 1300 to some third party factory. 1301 **/ 1302 @safe class DelegatingInstanceFactory(T, X : T) : InstanceFactory!T, MutableDecorator!(Factory!X) { 1303 1304 mixin AllocatorAwareMixin!(typeof(this)); 1305 mixin LocatorAwareMixin!(typeof(this)); 1306 1307 private { 1308 Factory!X decorated_; 1309 } 1310 1311 public { 1312 1313 /** 1314 Default constructor for DelegatingInstanceFactory!(T, X) 1315 **/ 1316 this() @safe { 1317 1318 } 1319 1320 /** 1321 Constructor for DelegatingInstanceFactory!(T, X) 1322 1323 Params: 1324 factory = the factory to which this instance will delegate the task of creating a component 1325 **/ 1326 this(Factory!X factory) @safe { 1327 this.decorated = factory; 1328 } 1329 1330 mixin MutableDecoratorMixin!(Factory!X); 1331 1332 /** 1333 Create a new instance of object of type T. 1334 1335 Returns: 1336 T instantiated component 1337 **/ 1338 T factory() @safe { 1339 debug(trace) trace("Delegating contruction of ", typeid(T), " to third party factory."); 1340 return this.decorated.factory(); 1341 } 1342 } 1343 } 1344 1345 /** 1346 Default implementation of destructor that calls dispose upon @safe classes only. 1347 **/ 1348 @safe class DefaultInstanceDestructor(T) : InstanceDestructor!T { 1349 1350 mixin AllocatorAwareMixin!(typeof(this)); 1351 mixin LocatorAwareMixin!(typeof(this)); 1352 1353 /** 1354 Destruct a component of type T and deallocate it using stored allocator. 1355 1356 Params: 1357 destructable = element to be destructed and deallocated using stored allocator 1358 **/ 1359 void destruct(ref T component) @trusted { 1360 import std.experimental.allocator : dispose; 1361 1362 static if (is(T == class)) { 1363 debug(trace) trace("Destroying existing component of ", typeid(T)); 1364 this.allocator.dispose(component); 1365 } 1366 1367 // Do nothing here. 1368 } 1369 } 1370 1371 /** 1372 Instance destructor that uses a callback to destroy and deallocate components of type T. 1373 **/ 1374 @safe class CallbackInstaceDestructor(T, Dg : void delegate(RCIAllocator, ref T destructable, Args), Args...) : InstanceDestructor!T { 1375 mixin AllocatorAwareMixin!(typeof(this)); 1376 mixin LocatorAwareMixin!(typeof(this)); 1377 mixin ParameterHolder!(Args); 1378 1379 private { 1380 Dg dg_; 1381 } 1382 1383 public { 1384 @property { 1385 /** 1386 Set dg 1387 1388 Params: 1389 dg = the delegate used to destroy component 1390 Returns: 1391 typeof(this) 1392 **/ 1393 typeof(this) dg(Dg dg) @safe nothrow pure { 1394 this.dg_ = dg; 1395 1396 return this; 1397 } 1398 1399 /** 1400 Get dg 1401 1402 Returns: 1403 Dg 1404 **/ 1405 Dg dg() @safe nothrow pure { 1406 return this.dg_; 1407 } 1408 } 1409 1410 /** 1411 Destruct a component of type T and deallocate it using stored allocator. 1412 1413 Params: 1414 destructable = element to be destructed and deallocated using stored allocator 1415 **/ 1416 void destruct(ref T destructable) @trusted { 1417 debug(trace) trace("Destroying ", typeid(T), " using function/delegate ", typeid(Dg), " with arguments of ", ", ".separated(this.args).wrapped); 1418 this.dg()(this.allocator, destructable, this.args); 1419 } 1420 } 1421 } 1422 1423 /** 1424 ditto 1425 **/ 1426 CallbackInstaceDestructor!(T, Dg, Args) callbackInstanceDestructor 1427 (T, Dg : void delegate(RCIAllocator, ref T destructable, Args), Args...) 1428 (Dg dg, Args args) { 1429 1430 auto callbackInstanceDestructor = 1431 new CallbackInstaceDestructor!(T, Dg, Args)() 1432 .dg(dg); 1433 1434 static if (Args.length > 0) { 1435 1436 callbackInstanceDestructor.args = args; 1437 } 1438 1439 return callbackInstanceDestructor; 1440 } 1441 1442 /** 1443 Instance destructor using an third party component to do destruction of components. 1444 **/ 1445 template FactoryMethodInstanceDestructor(string method, T, Z, Args...) 1446 if ( 1447 isSomeFunction!(getMember!(T, method)) && 1448 isMethodCompatible!(T, method, Z, Args) 1449 ) { 1450 1451 alias Compatible = getCompatibleOverload!(T, method, Z, Args); 1452 1453 @safe class FactoryMethodInstanceDestructor : InstanceDestructor!Z { 1454 1455 mixin LocatorAwareMixin!(typeof(this)); 1456 mixin AllocatorAwareMixin!(typeof(this)); 1457 mixin ParameterHolder!(Args); 1458 1459 public { 1460 1461 static if (!isStaticFunction!(Compatible)) { 1462 1463 T destructor_; 1464 1465 @property { 1466 /** 1467 Set destructor 1468 1469 Params: 1470 destructor = the component destructor used to destroy component 1471 1472 Returns: 1473 typeof(this) 1474 **/ 1475 typeof(this) destructor(T destructor) @safe nothrow pure { 1476 this.destructor_ = destructor; 1477 1478 return this; 1479 } 1480 1481 /** 1482 Get destructor 1483 1484 Returns: 1485 T 1486 **/ 1487 T destructor() @safe nothrow pure { 1488 return this.destructor_; 1489 } 1490 } 1491 } 1492 1493 /** 1494 Destruct a component of type T and deallocate it using stored allocator. 1495 1496 Params: 1497 destructable = element to be destructed and deallocated using stored allocator 1498 **/ 1499 void destruct(ref Z destructable) @trusted { 1500 debug(trace) trace("Destroying ", typeid(Z), " using destruction method ", method, " of ", typeid(T), " with arguments of ", ", ".separated(this.args).wrapped); 1501 static if (!isStaticFunction!(Compatible)) { 1502 __traits(getMember, this.destructor, method)(destructable, this.args); 1503 } else { 1504 __traits(getMember, T, method)(destructable, this.args); 1505 } 1506 } 1507 } 1508 } 1509 } 1510 1511 /** 1512 Create an instance destructor that uses third party component's method to do destruction of a component. 1513 1514 Create an instance destructor that uses third party component's method to do destruction of a component. 1515 1516 Params: 1517 Z = the type of destructed object 1518 method = method used from component T to destroy component Z 1519 destructor = type of destructor component 1520 arguments = list of arguments passed to destructor component's method 1521 1522 Returns: 1523 FactoryMethodInstanceDestructor!(method, T, Z, Args) 1524 **/ 1525 auto factoryMethodInstanceDestructor( 1526 Z, 1527 string method, 1528 T, 1529 Args... 1530 )( 1531 T destructor, 1532 Args arguments 1533 ) if (isNonStaticMethodCompatible!(T, method, Z, Args)) { 1534 1535 auto instanceDestructor = new FactoryMethodInstanceDestructor!(method, T, Z, Args)(); 1536 instanceDestructor.destructor = destructor; 1537 1538 static if (Args.length > 0) { 1539 1540 instanceDestructor.args = arguments; 1541 } 1542 1543 return instanceDestructor; 1544 } 1545 1546 /** 1547 ditto 1548 **/ 1549 auto factoryMethodInstanceDestructor( 1550 Z, 1551 string method, 1552 T, 1553 Args... 1554 )( 1555 Args arguments 1556 ) 1557 if (isStaticMethodCompatible!(T, method, Z, Args)) { 1558 1559 auto destructor = new FactoryMethodInstanceDestructor!(method, T, Z, Args)(); 1560 1561 static if (Args.length > 0) { 1562 destructor.args = arguments; 1563 } 1564 1565 return destructor; 1566 } 1567 1568 /** 1569 A check if the argument list passed to ConstructorBasedFactory or MethodConfigurer is compatible with signature of underlying 1570 method or constructor. 1571 1572 Note: 1573 For now it checks if the lengths are equal. For future it should also check if types are compatible. 1574 **/ 1575 template isArgumentListCompatible(alias func, ArgTuple...) 1576 if (isSomeFunction!func) { 1577 bool isArgumentListCompatible() @safe { 1578 alias FuncParams = Parameters!func; 1579 alias Required = Filter!(partialSuffixed!(isValueOfType, void), ParameterDefaults!func); 1580 1581 static if ((ArgTuple.length < Required.length) || (ArgTuple.length > FuncParams.length)) { 1582 1583 return false; 1584 } else { 1585 1586 bool result = true; 1587 foreach (index, Argument; ArgTuple) { 1588 1589 static if (!is(Argument : RuntimeReference) && !isImplicitlyConvertible!(Argument, FuncParams[index])) { 1590 1591 result = false; 1592 break; 1593 } 1594 } 1595 1596 return result; 1597 } 1598 } 1599 } 1600 1601 mixin template assertFieldCompatible(T, string field, Arg) { 1602 import aermicioi.aedi.util.traits; 1603 import std.traits; 1604 import std.meta; 1605 1606 static assert(isField!(T, field), name!T ~ "'s " ~ field ~ " member is not a field"); 1607 static assert(isProtection!(T, field, "public"), name!T ~ "'s " ~ field ~ " is not public and therefore cannot be accessed."); 1608 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); 1609 } 1610 1611 enum bool isFieldCompatible(T, string field, Arg) = 1612 isField!(T, field) && 1613 isProtection!(T, field, "public") && 1614 is(Arg : RuntimeReference) ? true : isImplicitlyConvertible!(Arg, typeof(getMember!(T, field))); 1615 1616 mixin template assertObjectConstructorCompatible(T, Args...) { 1617 import aermicioi.aedi.util.traits; 1618 import std.traits; 1619 import std.meta; 1620 1621 static assert(hasMember!(T, "__ctor"), name!T ~ " doesn't have any constructor to call."); 1622 static assert(isProtection!(T, "__ctor", "public"), name!T ~ "'s constructor is not public."); 1623 static assert(isSomeFunction!(__traits(getMember, T, "__ctor")), name!T ~ "'s constructor is not a function, probably a template."); 1624 static assert(variadicFunctionStyle!(__traits(getMember, T, "__ctor")) == Variadic.no, name!T ~ "'s constructor is a variadic function. Only non-variadic constructors are supported."); 1625 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."); 1626 } 1627 1628 enum bool isObjectConstructorCompatible(T, Args...) = isMethodCompatible!(T, "__ctor", Args); 1629 1630 mixin template assertObjectMethodCompatible(T, string method, Args...) { 1631 import std.range : only; 1632 import std.algorithm : joiner; 1633 import std.array : array; 1634 import aermicioi.aedi.util.traits; 1635 import std.traits; 1636 import std.meta; 1637 1638 static assert(hasMember!(T, method), name!T ~ "'s method " ~ method ~ " not found."); 1639 static assert(isProtection!(T, method, "public"), name!T ~ "'s method " ~ method ~ " is not public"); 1640 static assert(isSomeFunction!(__traits(getMember, T, method)), name!T ~ "'s member " ~ method ~ " is not a function, probably a field, or a template."); 1641 static assert(variadicFunctionStyle!(__traits(getMember, T, method)) == Variadic.no, name!T ~ "'s method " ~ method ~ "is variadic function. Only non-variadic methods are supported."); 1642 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."); 1643 } 1644 1645 enum bool isObjectMethodCompatible(T, string method, Args...) = isMethodCompatible!(T, method, Args); 1646 1647 template isMethodCompatible(T, string method, Args...) { 1648 enum bool isMethodCompatible = 1649 hasMember!(T, method) && 1650 isProtection!(T, method, "public") && 1651 isSomeFunction!(__traits(getMember, T, method)) && 1652 (variadicFunctionStyle!(__traits(getMember, T, method)) == Variadic.no) && 1653 (Filter!(partialSuffixed!(isArgumentListCompatible, Args), getOverloads!(T, method)).length == 1); 1654 } 1655 1656 template getCompatibleOverload(T, string method, Args...) 1657 if (isObjectMethodCompatible!(T, method, Args)) { 1658 1659 alias getCompatibleOverload = Filter!(partialSuffixed!(isArgumentListCompatible, Args), getOverloads!(T, method))[0]; 1660 } 1661 1662 alias isStaticMethodCompatible = templateAnd!( 1663 isMethodCompatible, 1664 chain!( 1665 isStaticFunction, 1666 getCompatibleOverload 1667 ) 1668 ); 1669 1670 alias isNonStaticMethodCompatible = templateAnd!( 1671 isMethodCompatible, 1672 chain!( 1673 templateNot!isStaticFunction, 1674 getCompatibleOverload 1675 ) 1676 ); 1677 1678 alias isNonStaticMethodCompatibleAndReturnTypeOf(X) = templateAnd!( 1679 isMethodCompatible, 1680 chain!( 1681 templateNot!isStaticFunction, 1682 getCompatibleOverload 1683 ), 1684 partialSuffixed!( 1685 chain!( 1686 partialPrefixed!( 1687 get, 1688 0 1689 ), 1690 getCompatibleOverload 1691 ), 1692 X 1693 ) 1694 ); 1695 1696 private { 1697 template isValueOfType(alias value, Type) { 1698 enum bool isValueOfType = is(typeof(value) == Type); 1699 } 1700 1701 template isValueOfType(Value, Type) { 1702 enum bool isValueOfType = is(Value == Type); 1703 } 1704 1705 enum bool isStruct(T) = is(T == struct); 1706 1707 1708 template hasDefaultCtor(T) { 1709 static if (!__traits(hasMember, T, "__ctor")) { 1710 enum found = true; 1711 } 1712 1713 static if (!is(typeof(found)) && is(T == struct)) { 1714 enum found = true; 1715 } 1716 1717 static if (!is(typeof(found)) && isProtection!(T, "__ctor", "public")) { 1718 static foreach (overload; __traits(getOverloads, T, "__ctor")) { 1719 static if (!is(typeof(found)) && (variadicFunctionStyle!overload == Variadic.no)) { 1720 static if (arity!overload == 0) { 1721 enum found = true; 1722 } 1723 } 1724 } 1725 } 1726 1727 static if (!is(typeof(found))) { 1728 enum found = false; 1729 } 1730 1731 enum hasDefaultCtor = found; 1732 } 1733 } 1734 1735 private { 1736 string compileArgumentsTuple(Tuple...)(string types, string array, string locator) @safe { 1737 import std.conv : to; 1738 import std.array : join; 1739 string[] stmt; 1740 1741 foreach (index, Type; Tuple) { 1742 stmt ~= array ~ "[" ~ index.to!string ~ "].resolve!(" ~ types ~ "[" ~ index.to!string ~ "])(" ~ locator ~ ")"; 1743 } 1744 1745 return stmt.join(", "); 1746 } 1747 }