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 }