Module configuration_primitives

Aedi, a dependency injection library.

Aedi is a dependency injection library. It does provide a set of containers that do IoC, and an interface to configure application components (structs, objects, etc.)


The aim of library is to provide a dependency injection solution that is feature rich, easy to use, easy to learn, and easy to extend up to your needs.

Besides set , construct , methods used in all above examples, framework offers several other configuration methods, that allow greater manipulation, or less verbosity. Additional configu- ration primitives are listed below:

  1. autowire
  2. factoryMethod
  3. callback
  4. value
  5. destructor

Lets start with first one from list, which is autowire method. From it's name we can deduce that it automates wiring of components somehow. But how does it do this? Let's register a car using old set and construct methods:

    .construct(lref!Size, lref!Engine)


Even with more structured instantiation code present using framework, sometimes, even this code is too verbose. Some components for example have a set of dependencies defined by some interface, and having for each dependency to reference it using lref!Type is quite cumbersome, and painfull.

And here comes into play autowire configuration method, which handles referencing of component by type on behalf of developer that creates the configuration, like in example below:

register!Car("sedan.engine.default") // Register a car with a default engine.
    .autowire() // Construct Car using default implementation of a size, and engine.
    .autowire!"color"(); // Inject default implementation of a Color.

In current implementation of autowired due to limitations of current compiler’s compile time introspection, autowire cannot work well with overloaded methods or constructors. In such cases, autowire cannot know which version of method/constructor to overload, and will default to the first one from the list of overloads.


Frequently in applications that use third party code, components from that third party code, are instantiated using factories or other entities from that third party code, and the client code of those libraries simply are not allowed to manually create those types. To aleviate this problem, when such a component is registered in a container, framework allows such components to be instantiated by using third party code responsible for this task.

Supposedly, the car simulation application introduced previously, is upgraded with a car man- ufacturing simulation, and new cars that are needed to be registered as components into container are required to be instantiated using car manufacturing component. factoryMethod method instructs framework to use a third party component to instantiate registered component, using static method of a factory, or by using an instance of a factory. In example usage of factoryMethod is shown:

register!Car("car_manufacturer.product.sedan") // Register a car with gasoline engine
    .factoryMethod!(CarManufacturer, "manufacture")(lref!CarManufacturer, "size.sedan".lref);


While just by configuring a component with simple values or references, in some cases during construction of a component some custom logic must be run, that is or not related to instantiation process. callback configuration method can be used to pass a function or a delegate that constructs or configures the component, according to some custom logic, not expressable by already available configuration primitives. Example below shows how a callback can be used as a constructor or as a configuration primitive.

register!Car("sedan.engine.electric") // Register a car with electric engine
        delegate (Locator!() loc) { // Let's construct our car using some custom logic that isn't possible to express with a simple construct method call.
            Car car = new Car(loc.locate!Size, new ElectricEngine());

            return car;
    ).callback( // Let's configure newly built car with a custom color.
        delegate (Locator!() loc, Car car) {
            car.color = loc.locate!Color;

Though it's quite a simple primitive, it is as well powerful one. It is possible to define your own custom configuration primitives just by using under the hood the callback primitive.


The value primitive instructs container to use as basis an already constructed component. The component itself can be further configured using primitives specified above. The only specific case that should be taken into accout is the fact that using same value that is a reference type (a pointer, or class) in multiple components, will result into having same component with multiple identities in container. The container itself won't be aware of the relationship, as in case with aliasing. It is recommended that value to be used only once per reference value.

    .value(new GasolineEngine(15)); // Register a gasoline engine. Note: this engine is not the same gasoline engine from default implementation.


When container is shut down, the default behavior is to destroy all constructed components. The default destruction logic is to run destructor and deallocate the component. Sometimes, the default behavior is not desired, or additional logic should be run. In such cases destructor primitive can be used to either pass a delegate, or factory method to be used to destroy the component.

register!Car("car_manufacturer.product.sedan") // Register a car with gasoline engine
    .factoryMethod!(CarManufacturer, "manufacture")(lref!CarManufacturer, "size.sedan".lref)
    .destructor((IAllocator alloc, ref Car car) {
        write("Crushing the car. ");

Those additional primitives are quite handy to use, with libraries that are usign factories, dependencies by interface, or when some custom behavior has to be run during construction of a component. Output below is the result of using those primitives, to run.

Uuh, what a nice car, Gasoline car with following specs:
Size:	Size(200, 150, 300)
Color:	Color(0, 0, 0)
Engine:	app.GasolineEngine
Let`s turn it on
What a nice sound! We should make a test drive!
Umm the test drive was awesome, let`s get home and turn it off.

Uuh, what a nice car, Manufactured car with following specs:
Size:	Size(200, 150, 500)
Color:	Color(0, 0, 0)
Engine:	app.DieselEngine
Let`s turn it on
What a nice sound! We should make a test drive!
Umm the test drive was awesome, let`s get home and turn it off.

Uuh, what a nice car, Electric car with following specs:
Size:	Size(200, 150, 300)
Color:	Color(0, 0, 0)
Engine:	app.ElectricEngine
Let`s turn it on
What a nice sound! We should make a test drive!
Umm the test drive was awesome, let`s get home and turn it off.

If you are puzzled from where is this output, take a look at the source code of this example. Modify it, play with it to understand these configuration primitives.


drive(car, name)


Engine Interface for engines.


Car A class representing a car.
CarManufacturer A manufacturer of cars.
DieselEngine A concrete implementation of Engine that uses diesel for propelling.
ElectricEngine A concrete implementation of Engine that uses electricity for propelling.
GasolineEngine A concrete implementation of Engine that uses gasoline for propelling.


Color A struct that should be managed by container.
Size Size of a car.