boost-ext / di

C++14 Dependency Injection Library
https://boost-ext.github.io/di
1.13k stars 136 forks source link

Define an injector (or scope instance) as a class field? #319

Closed scottmcnab closed 6 years ago

scottmcnab commented 6 years ago

In every example code provided, the return type for boost::di::make_injector() is always auto. For example:

  const auto injector = di::make_injector(
    di::bind<class Greater>().to<hello>()
  );

This makes it impossible to store the injector instance in a class field. For example, I would like to be able to do something like the following:

// MyApp.h
class MyApp {
    di::injector<??> m_injector;    // What type to use here??
public:
    MyApp();
    void DoSomethingRequringInjector();
}

// MyApp.cpp
#include "MyApp.h"

MyApp::MyApp() : m_injector(di::make_injector(...))
{
}

void MyApp::DoSomethingRequringInjector()
{
    auto foo = m_injector.create<std::unique_ptr<Foo>>();
    // do something with foo
}

Specifically, I would like to manage the lifetime of injector as a class field in order to be able to use scoped scope instead of di::singleton (as this is causing problems by delaying destruction of all singleton objects until C runtime shutdown).

Since the actual concrete type returned from di::make_injector(...) is potentially complex, I don't know how to declare this as a usable type?

It would appear that the session_scope extension might offer a solution:

auto ms = di::extension::session(my_session)();

However again the type returned from this expression (auto ms) is also not-explicit. This prevents me from doing some thing like:

// MyApp.h
class MyApp {
    di::extension::session_scope<???>m_session;    // What type to use here??
public:
    MyApp();
    void DoSomethingRequringScopedInjector();
}

// MyApp.cpp
#include "MyApp.h"

MyApp::MyApp() : m_session(di::extension::session(my_session)())
{
}

void MyApp::DoSomethingRequringScopedInjector()
{
    // Injector singleton types defined here use .in(di::extension::session(my_session{});
    auto injector = di::make_injector(...);
    auto foo = m_injector.create<std::unique_ptr<Foo>>();
    // do something with foo
}

I feel I am missing something here? Is there some way to declare and store these instances without using auto keyword?

Thanks!

Specifications

Shelim commented 6 years ago

It is possible, I scour the documentation for source, but AFAIK you can do that:

di::injector<std::shared_ptr<interface1>, std::shared_ptr<interface2> > m_injector;
m_injector.create<std::shared_ptr<interface1> >();
m_injector.create<std::shared_ptr<interface2> >();

The arguments for template are all types you will be created explicity. So, if interface1 needs interface3 and interface4 in constructor, you do not need to include them, as long as you won't create any of them directly by create()

Hope it helps

Shelim commented 6 years ago

Ah, and you need to move injector, it is not copy-able

scottmcnab commented 6 years ago

OK I think I had a problem with that not resolving non-explicitly declared interfaces, but I'll try again.

Is it possible to create explicit scopes too? This would be very useful.

Thanks

On 28 March 2018 at 13:34, Piotr Kosek notifications@github.com wrote:

Ah, and you need to move injector, it is not copy-able

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/boost-experimental/di/issues/319#issuecomment-376767006, or mute the thread https://github.com/notifications/unsubscribe-auth/AHEFp_ZurB0cvaPPlcaBu3-LhZK1VGTGks5tiyDigaJpZM4S9SNy .

krzysztof-jusiak commented 6 years ago

Right, as @Shelim pointed out. It's possible with di::injector<EXPOSED_TYPES...>. More about modules here -> http://boost-experimental.github.io/di/user_guide/index.html#modules. By default, with auto, all type are being exposed. Please, take a look at a full example here -> https://github.com/boost-experimental/di/tree/cpp14/example/modules