Boost logo

Boost :

From: Krzysztof Jusiak (krzysztof_at_[hidden])
Date: 2021-02-23 07:49:01


>
> That's not exactly what I meant.
>
> What I meant is that if library A uses DI mechanism A, can I describe to
> a Boost.DependencyInjection that mechanism A, such that mechanism A maps
> 100% into Boost.DependencyInjection.
>
> To put it back into terms of Allocators, how do I tell a
> Boost.DependencyInjection that for all std::vector<T, X> where X meets
> the Allocator Concept, std::vector<T, X> can have an instance X injected
> into its construction such that one can now write:
>
> const auto injector = di::make_injector(
> di::bind<std::vector>.to<MyAllocator>()
> );
> injector.create<std::vector<int>>()
>
> ... and that makes me a std::vector<int, MyAllocator<int>>.
>
> I'm sure it's probably possible, it's just I from my best reading of the
> docs, I think it's quite awkward to do right now. And I think it ought
> to not be awkward in anything Boost approves.
>
>
With DI that can be already achieved with constructor/named template
deduction.

Let's try it then, shall we?

For example, we have a custom allocator my_allocator

template<class T> struct my_allocator : std::allocator<T> {};

And 2 dependencies dep1, dep2 were we want a custom allocator to be
injected (for vector and set)

template<class TAllocator = class Allocator>
struct dep1 {
  explicit(true) dep1(std::vector<int, TAllocator>) {}
};

template<class TAllocator = class Allocator>
struct dep2 {
  dep2(const std::set<int, std::less<int>, TAllocator>&,
std::unique_ptr<interface> i) {
    assert(dynamic_cast<implementation*>(i.get()));
  }
};

and the application app1, app2 (notice different constructor parameters)

template<class TAllocator = class Allocator>
struct app1 {
  app1(dep1<TAllocator>, dep2<TAllocator>) {}
};

template<class TAllocator = class Allocator>
struct app2 {
  app2(std::shared_ptr<dep2<TAllocator>>, const dep1<TAllocator>&) {}
};

Let's make the injector and override Allocator with my_allocator

auto injector = di::make_injector(
   di::bind<class Allocator>.to<my_allocator<int>>() // won't compile if
missing (that's what means compile-time DI, it won't be an exception it
will be a compilation error
 , di::bind<interface>.to<implementation>() // won't compile
if missing
);

int main() {
  auto app1 = injector.create<app1>(); // Okay will create app1 with
my_allocator injected into all dependencies which are using class Allocator
  auto app2 = injector.create<app2>(); // Okay will create app2, just a
show-case that the order and types of constructor parameters doesn't matter
for DI
}

Full example -> https://godbolt.org/z/sPszoG

Right, now for 2e2 testing we would like to change the allocator for
test_allocator but keep all other dependencies

auto test_injector = di::make_injector(
   std::move(injector), // old injector
   di::bind<class Allocator>.to<test_allocator>() [di::override]
);

int main() {
  auto app1 = test_injector.create<app1>(); // Okay will create app1 with
test_allocator injected into all dependencies which are using class
Allocator
}


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk