zhongxingzhi / di

C++ Dependency Injection

Home Page:http://krzysztof-jusiak.github.io/di/cpp14/boost/libs/di/doc/html

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Boost Licence Github Release Bii Try Boost.DI Online Gitter Chat

Boost.DI: C++ Dependency Injection

Boost Libraries Build Status Build Status Coveralls Github Issues

"Don't call us, we'll call you", Hollywood principle

Dependency Injection (DI) involves passing (injecting) one or more dependencies (or services) to a dependent object (or client) which become part of the client’s state. It is like the Strategy Pattern, except the strategy is set once, at construction. DI enables loosely coupled designs, which are easier to maintain and test.

Dependency Injection

"Let's make some coffee!"

coffee maker

```cpp No Dependency injection | Dependency Injection ----------------------------------------|-------------------------------------------- class coffee_maker { | class coffee_maker { public: | public: void brew() { | coffee_maker(shared_ptr heater heater->on(); | , unique_ptr pump) pump->pump(); | : heater(heater), pump(move(pump)) clog << "coffee"! << endl; | { } heater->off(); | } | void brew() { | heater->on(); private: | pump->pump(); shared_ptr heater = | clog << "coffee!" << endl; make_shared(); | heater->off(); | } unique_ptr pump = | make_unique(heater); | private: }; | shared_ptr heater; | unique_ptr pump; | }; | ```

Why Dependency Injection?

  • DI provides loosely coupled code (separation of business logic and object creation)
  • DI provides easier to maintain code (different objects might be easily injected)
  • DI provides easier to test code (fakes objects might be injected)
The Clean Code Talks - Don't Look For Things! DAGGER 2 - A New Type of dependency injection

Why Boost.DI?

  • Boost.DI has none or minimal run-time overhead - Run-time performance
  • Boost.DI compiles fast - Compile-time performance
  • Boost.DI gives short diagnostic messages - Diagnostic messages
  • Boost.DI is not intrusive [link to tutorial]
  • Boost.DI reduces boilerplate code [link to tutorial]
  • Boost.DI reduces testing effort [link to tutorial]
  • Boost.DI gives better control of what and how is created [link to tutorial]
  • Boost.DI gives better understanding about objects hierarchy [link to tutorial]

How To Start?

To get started the only file you need is di.hpp:

https://raw.githubusercontent.com/krzysztof-jusiak/di/cpp14/include/boost/di.hpp

    // main.cpp
    #include "di.hpp"
    int main() { }
    $CXX -std=c++14 -I. main.cpp

To get and test Boost.DI library:

    git clone https://github.com/krzysztof-jusiak/di.git
    cd build && cmake ..
    make all test

Quick User Guide | Examples

Let's assume all examples below include boost/di.hpp header and define di namespace alias.

#include <boost/di.hpp>
namespace di = boost::di;
//
struct i1 { virtual ~i1() = default; virtual void dummy1() = 0; };
struct i2 { virtual ~i2() = default; virtual void dummy2() = 0; };
struct impl1 : i1 { void dummy1() override { } };
struct impl2 : i2 { void dummy2() override { } };
struct impl : i1, i2 { void dummy1() override { } void dummy2() override { } };

Injector

Create empty injector                   | Test
----------------------------------------|-----------------------------------------
auto injector = di::make_injector();    | assert(0 == injector.create<int>());

Bindings | Examples | More examples

Bind interface to implementation        | Test
----------------------------------------|-----------------------------------------
auto injector = di::make_injector(      | auto object = injector.create<unique_ptr<i1>>();
    di::bind<i1, impl1>                 | assert(dynamic_cast<impl1*>(object.get()));
);                                      |
Bind different interfaces to one        | Test
implementation                          |
----------------------------------------|-----------------------------------------
auto injector = di::make_injector(      | auto object1 = injector.create<shared_ptr<i1>>();
    di::bind<di::any_of<i1, i2>, impl>  | auto object2 = injector.create<shared_ptr<i2>>();
);                                      | assert(dynamic_cast<impl*>(object1.get()));
                                        | assert(dynamic_cast<impl*>(object2.get()));
Bind type to compile time value         | Test
----------------------------------------|-----------------------------------------
template<int N> using int_ =            | assert(42 == injector.create<int>());
    integral_constant<int, N>;          |
                                        |
auto injector = di::make_injector(      |
    di::bind<int, int_<42>>             |
);                                      |
Bind type to value (see external scope) | Test
----------------------------------------|-----------------------------------------
auto injector = di::make_injector(      | assert(42 == injector.create<int>());
    di::bind<int>.to(42)                |
);                                      |
Cross platform `bind` (Clang/GCC/MSVC)  | Test
----------------------------------------|-----------------------------------------
auto injector = di::make_injector(      | auto object = injector.create<unique_ptr<i1>>();
    di::bind<i1, impl1>() // brackets   | assert(dynamic_cast<impl1*>(object.get()));
    // MSVC 2015 doesnt support variable|
    // templates                        |
);                                      |

Injections | Examples | More examples

Direct constructor injection            | Test
----------------------------------------|-----------------------------------------
struct c {                              | auto object = injector.create<c>();
    c(int a, double d) : a(a), d(d) { } | assert(42 == object.a);
                                        | assert(87.0 == object.d);
    int a = 0;                          |
    double d = 0.0;                     |
};                                      |
                                        |
auto injector = di::make_injector(      |
    di::bind<int>.to(42)                |
  , di::bind<double>.to(87.0)           |
);                                      |
Aggregate constructor injection         | Test
----------------------------------------|-----------------------------------------
struct c {                              | auto object = injector.create<c>();
    int a = 0;                          | assert(42 == object.a);
    double d = 0.0;                     | assert(87.0 == object.d);
};                                      |
                                        |
auto injector = di::make_injector(      |
    di::bind<int>.to(42)                |
  , di::bind<double>.to(87.0)           |
);                                      |
Direct constructor injection with many  | Test
constructors (longest parameters list   |
constructor will be chosen)             |
----------------------------------------|-----------------------------------------
struct c {                              | auto object = injector.create<c>();
    c();                                | assert(42 == object.a);
    c(int a) : a(a) { }                 | assert(87.0 == object.d);
    c(int a, double d) : a(a), d(d) { } |
                                        |
    int a = 0;                          |
    double d = 0.0;                     |
};                                      |
                                        |
auto injector = di::make_injector(      |
    di::bind<int>.to(42)                |
  , di::bind<double>.to(87.0)           |
);                                      |
Direct constructor injection with       | Test
ambiguous constructors (BOOST_DI_INJECT)|
----------------------------------------|-----------------------------------------
struct c {                              | auto object = injector.create<c>();
    c(double d, int a) : a(a), d(d) { } | assert(42 == object.a);
    BOOST_DI_INJECT(c, int a, double d) | assert(87.0 == object.d);
        : a(a), d(d) { }                |
                                        |
    int a = 0;                          |
    double d = 0.0;                     |
};                                      |
                                        |
auto injector = di::make_injector(      |
    di::bind<int>.to(42)                |
  , di::bind<double>.to(87.0)           |
);                                      |
Direct constructor injection with       | Test
ambiguous constructors                  |
(BOOST_DI_INJECT_TRAITS)                |
----------------------------------------|-----------------------------------------
struct c {                              | auto object = injector.create<c>();
    BOOST_DI_INJECT_TRAITS(int, double);| assert(42 == object.a);
    c(double d, int a) : a(a), d(d) { } | assert(87.0 == object.d);
    c(int a, double d) : a(a), d(d) { } |
                                        |
    int a = 0;                          |
    double d = 0.0;                     |
};                                      |
                                        |
auto injector = di::make_injector(      |
    di::bind<int>.to(42)                |
  , di::bind<double>.to(87.0)           |
);                                      |
Direct constructor injection with       | Test
ambiguous constructors                  |
(di::ctor_traits)                       |
----------------------------------------|-----------------------------------------
struct c {                              | auto object = injector.create<c>();
    c(double d, int a) : a(a), d(d) { } | assert(42 == object.a);
    c(int a, double d) : a(a), d(d) { } | assert(87.0 == object.d);
                                        |
    int a = 0;                          |
    double d = 0.0;                     |
};                                      |
                                        |
namespace boost { namespace di {        |
template<>                              |
struct ctor_traits<c> {                 |
    BOOST_DI_INJECT_TRAITS(int, double);|
};                                      |
}} // boost::di                         |
                                        |
auto injector = di::make_injector(      |
    di::bind<int>.to(42)                |
  , di::bind<double>.to(87.0)           |
);                                      |

Annotations | Examples

Annotated constructor injection         | Test
----------------------------------------|-----------------------------------------
auto int1 = []{};                       | auto object = injector.create<c>();
auto int2 = []{};                       | assert(42 == object.a);
                                        | assert(87 == object.b);
struct c {                              |
    BOOST_DI_INJECT(c                   |
        , (named = int1) int a          |
        , (named = int2) int b)         |
        : a(a), b(b)                    |
    { }                                 |
                                        |
    int a = 0;                          |
    int b = 0;                          |
};                                      |
                                        |
auto injector = di::make_injector(      |
    di::bind<int>.named(int1).to(42)    |
  , di::bind<int>.named(int2).to(87)    |
);                                      |
Annotated constructor injection with    | Test
the same names for different parameters |
----------------------------------------|-----------------------------------------
auto n1 = []{};                         | auto object = injector.create<c>();
auto n2 = []{};                         | assert(42 == object.i1);
                                        | assert(42 == object.i2);
struct c {                              | assert(87 == object.i3);
  BOOST_DI_INJECT(c                     | assert(0 == object.i4);
      , (named = n1) int a              | assert("str" == c.s);
      , (named = n1) int b              |
      , (named = n2) int c              |
      , int d                           |
      , (named = n1) string s)          |
  : i1(i1), i2(i2), i3(i3), i4(i4), s(s)|
  { }                                   |
                                        |
  int i1 = 0;                           |
  int i2 = 0;                           |
  int i3 = 0;                           |
  int i4 = 0;                           |
  string s;                             |
};                                      |
                                        |
auto injector = di::make_injector(      |
    di::bind<int>.named(n1).to(42)      |
  , di::bind<int>.named(n2).to(87)      |
  , di::bind<string>.named(n1).to("str")|
);                                      |
Annotated constructor injection with    | Test
separate constructor definition         |
----------------------------------------|-----------------------------------------
auto int1 = []{};                       | auto object = injector.create<c>();
auto int2 = []{};                       | assert(42 == object.a);
                                        | assert(87 == object.b);
struct c {                              |
    BOOST_DI_INJECT(c                   |
        , (named = int1) int a          |
        , (named = int2) int b);        |
                                        |
    int a = 0;                          |
    int b = 0;                          |
};                                      |
                                        |
c::c(int a, int b) : a(a), b(b) { }     |
                                        |
auto injector = di::make_injector(      |
    di::bind<int>.named(int1).to(42)    |
  , di::bind<int>.named(int2).to(87)    |
);                                      |
Annotated constructor injection with    | Test
di::ctor_traits                         |
----------------------------------------|-----------------------------------------
auto int1 = []{};                       | auto object = injector.create<c>();
auto int2 = []{};                       | assert(42 == object.a);
                                        | assert(87 == object.b);
struct c {                              |
    c(int a, int b) : a(a), b(b) { }    |
                                        |
    int a = 0;                          |
    int b = 0;                          |
};                                      |
                                        |
namespace boost { namespace di {        |
template<>                              |
struct ctor_traits<c> {                 |
    BOOST_DI_INJECT_TRAITS(             |
        (named = int1) int              |
      , (named = int2) int);            |
};                                      |
}} // boost::di                         |
                                        |
auto injector = di::make_injector(      |
    di::bind<int>.named(int1).to(42)    |
  , di::bind<int>.named(int2).to(87)    |
);                                      |

Scopes | Examples | More examples

Deduce scope (default)                  | Test
----------------------------------------|-----------------------------------------
struct c {                              | auto object1 = injector.create<unique_ptr<c>>();
    shared_ptr<i1> sp; /*singleton*/    | auto object2 = injector.create<unique_ptr<c>>();
    unique_ptr<i2> up; /*unique*/       | assert(object1->sp == object2->sp);
    int& i; /*singleton*/               | assert(object1->up != object2->up);
    double d; /*unique*/                | assert(42 == object1->i);
};                                      | assert(&i == &object1->i;
                                        | assert(42 == object2->i);
auto i = 42;                            | assert(&i == &object2->i);
                                        | assert(87.0 == object1->d);
auto injector = di::make_injector(      | assert(87.0 == object2->d);
    di::bind<i1, impl1>                 |
  , di::bind<i2, impl2>                 |
  , di::bind<int>.to(i)                 |
  , di::bind<double>.to(87.0)           |
);                                      |
Type Deduced scope
T unique
T& singleton
const T& unique (temporary)/singleton
T* unique (ownership transfer)
const T* unique (ownership transfer)
T&& unique
unique_ptr unique
shared_ptr singleton
weak_ptr singleton
Unique scope                            | Test
----------------------------------------|-----------------------------------------
auto injector = di::make_injector(      | assert(injector.create<shared_ptr<i1>>()
    di::bind<i1, impl1>.in(di::unique)  |        !=
);                                      |        injector.create<shared_ptr<i1>>()
                                        | );
Shared scope (shared per one thread)    | Test
----------------------------------------|-----------------------------------------
auto injector = di::make_injector(      | assert(injector.create<shared_ptr<i1>>()
    di::bind<i1, impl1>.in(di::shared)  |        ==
);                                      |        injector.create<shared_ptr<i1>>()
                                        | );
Singleton scope (shared between threads)| Test
----------------------------------------|-----------------------------------------
auto injector = di::make_injector(      | assert(injector.create<shared_ptr<i1>>()
   di::bind<i1, impl1>.in(di::singleton)|        ==
);                                      |        injector.create<shared_ptr<i1>>()
                                        | );
Session scope                           | Test
----------------------------------------|-----------------------------------------
auto my_session = []{};                 | assert(nullptr == injector.create<shared_ptr<i1>>());
                                        |
auto injector = di::make_injector(      | injector.call(di::session_entry(my_session));
    di::bind<i1, impl1>.in(             |
        di::session(my_session)         | assert(injector.create<shared_ptr<i1>>()
    )                                   |        ==
);                                      |        injector.create<shared_ptr<i1>>()
                                        | );
                                        |
                                        | injector.call(di::session_exit(my_session));
                                        |
                                        | assert(nullptr == injector.create<shared_ptr<i1>>());
External scope                          | Test
----------------------------------------|-----------------------------------------
auto l = 42l;                           | assert(42 == injector.create<int>());
auto b = false;                         | assert(injector.create<shared_ptr<i1>>()
                                        |        ==
auto injector = di::make_injector(      |        injector.create<shared_ptr<i1>>()
   di::bind<int>.to(0)                  | );
 , di::bind<int>.to(42) [di::override]  | assert(l == injector.create<long&>());
 , di::bind<i1>.to(make_shared<impl>()) | assert(&l == &injector.create<long&>());
 , di::bind<long>.to(l)                 | assert(87 == injector.create<short>());
 , di::bind<short>.to([]{return 87;})   | {
 , di::bind<i2>.to(                     | auto object = injector.create<shared_ptr<i2>>();
     [&](const auto& injector)          | assert(nullptr == object);
        -> shared_ptr<i2> {             | }
            if (b) {                    | {
              return injector.template  | b = true;
                create<                 | auto object = injector.create<shared_ptr<i2>>();
                  shared_ptr<impl2>>(); | assert(dynamic_cast<impl2*>(object.get()));
            }                           | }
            return nullptr;             |
     }                                  |
   )                                    |
);                                      |
Custom scope                            | Test
----------------------------------------|-----------------------------------------
struct custom_scope {                   | assert(injector.create<shared_ptr<i1>>()
  template<class TExpected, class>      |        !=
  struct scope {                        |        injector.create<shared_ptr<i1>>()
    template<class T, class TProvider>  | );
    auto create(const TProvider& pr) {  |
      return                            |
        shared_ptr<TExpected>{pr.get()};|
    }                                   |
  };                                    |
};                                      |
                                        |
auto injector = di::make_injector(      |
  di::bind<i1, impl1>.in(custom_scope{})|
);                                      |
Type/Scope unique shared singleton session external
T - - -
T& -
const T& ✔ (temporary) - - -
T* (transfer ownership) - - -
const T* - - -
T&& - - - -
unique_ptr - - -
shared_ptr
weak_ptr -

Modules | Examples

Module                                  | Test
----------------------------------------|-----------------------------------------
struct c {                              | auto object = injector.create<unique_ptr<c>>();
    c(unique_ptr<i1> i1                 | assert(dynamic_cast<impl1*>(object->i1.get()));
    , unique_ptr<i2> i2                 | assert(dynamic_cast<impl2*>(object->i2.get()));
    , int i) : i1(move(i1))             | assert(42 == object->i);
             , i2(move(i2)), i(i)       |
    { }                                 | auto up1 = injector.create<unique_ptr<i1>>();
                                        | assert(dynamic_cast<impl1*>(up1.get()));
    unique_ptr<i1> i1;                  |
    unique_ptr<i2> i2;                  | auto up2 = injector.create<unique_ptr<i2>>();
    int i = 0;                          | assert(dynamic_cast<impl2*>(up2.get()));
};                                      |
                                        |
struct module1 {                        |
    auto configure() const noexcept {   |
        return di::make_injector(       |
            di::bind<i1, impl1>         |
          , di::bind<int>.to(42)        |
        );                              |
    }                                   |
};                                      |
                                        |
struct module2 {                        |
    auto configure() const noexcept {   |
        return di::make_injector(       |
            di::bind<i2, impl2>         |
        );                              |
    };                                  |
};                                      |
                                        |
auto injector = di::make_injector(      |
    module1{}, module2{}                |
);                                      |
Exposed type module                     | Test
----------------------------------------|-----------------------------------------
struct c {                              | auto object = injector.create<c>();
    c(shared_ptr<i1> i1                 | assert(dynamic_cast<impl1*>(object.i1.get()));
    , shared_ptr<i2> i2                 | assert(dynamic_cast<impl2*>(object.i2.get()));
    , int i) : i1(i1), i2(i2), i(i)     | assert(42 == object.i);
    { }                                 |
                                        | // injector.create<unique_ptr<i1>>() // compile error
    shared_ptr<i1> i1;                  | // injector.create<unique_ptr<i2>>() // compile error
    shared_ptr<i2> i2;                  |
    int i = 0;                          |
};                                      |
                                        |
struct module {                         |
    di::injector<c> configure()         |
    const noexcept;                     |
                                        |
    int i = 0;                          |
};                                      |
                                        |
di::injector<c> // expose c             |
module::configure() const noexcept {    |
    return di::make_injector(           |
        di::bind<i1, impl1>             |
      , di::bind<i2, impl2>             |
      , di::bind<int>.to(i)             |
    );                                  |
}                                       |
                                        |
auto injector = di::make_injector(      |
    module{42}                          |
);                                      |
Exposed many types module               | Test
----------------------------------------|-----------------------------------------
struct module {                         | auto up1 = injector.create<unique_ptr<i1>>();
    di::injector<i1, i2> configure()    | assert(dynamic_cast<impl1*>(up1.get()));
    const noexcept;                     |
                                        | auto up2 = injector.create<unique_ptr<i2>>();
    int i = 0;                          | assert(dynamic_cast<impl2*>(up2.get()));
};                                      |
                                        |
di::injector<i1, i2> // expose i1, i2   |
module::configure() const noexcept {    |
    return di::make_injector(           |
        di::bind<i1, impl1>             |
      , di::bind<i2, impl2>             |
    );                                  |
}                                       |
                                        |
auto injector = di::make_injector(      |
    module{}                            |
);                                      |
Exposed type module with annotation     | Test
----------------------------------------|-----------------------------------------
auto my = []{};                         | auto object = injector.create<unique_ptr<c>>();
                                        | assert(dynamic_cast<impl1*>(object->up.get()));
struct c {                              |
    BOOST_DI_INJECT(c                   |
      , (named = my) unique_ptr<i1> up) |
      : up(up)                          |
    { }                                 |
                                        |
    unique_ptr<i1> up;                  |
};                                      |
                                        |
di::injector<i1> module =               |
    di::make_injector(                  |
        di::bind<i1, impl1>             |
    );                                  |
                                        |
auto injector = di::make_injector(      |
    di::bind<i1>.named(my).to(module)   |
);                                      |

Providers | Examples

Heap no throw provider                  | Test
----------------------------------------|-----------------------------------------
class heap_no_throw {                   | // per injector policy
public:                                 | auto injector = di::make_injector<my_provider>();
  template<                             | assert(0 == injector.create<int>());
    class // interface                  |
  , class T // implementation           | // global policy
  , class TInit // direct()/uniform{}   | #define BOOST_DI_CFG my_provider
  , class TMemory // heap/stack         | auto injector = di::make_injector();
  , class... TArgs>                     | assert(0 == injector.create<int>());
  auto get(const TInit&                 |
         , const TMemory&               |
         , TArgs&&... args)             |
  const noexcept {                      |
      return new (nothrow)              |
        T{forward<TArgs>(args)...};     |
  }                                     |
};                                      |
                                        |
class my_provider : public di::config { |
public:                                 |
    auto provider() const noexcept {    |
        return heap_no_throw{};         |
    }                                   |
};                                      |

Policies | Examples | More examples

Define policies configuration           | Test
(dump types)                            |
----------------------------------------|-----------------------------------------
class print_types_policy                | // per injector policy
    : public di::config {               | auto injector = di::make_injector<print_types_policy>();
public:                                 | injector.create<int>(); // output: int
  auto policies() const noexcept {      |
    return di::make_policies(           | // global policy
      [](auto type){                    | #define BOOST_DI_CFG my_policy
         using T = decltype(type);      | auto injector = di::make_injector();
         using arg = typename T::type;  | injector.create<int>(); // output: int
         cout << typeid(arg).name()     |
              << endl;                  |
      }                                 |
    );                                  |
  }                                     |
};                                      |
Define policies configuration           | Test
(dump types extended)                   |
----------------------------------------|-----------------------------------------
class print_types_info_policy           | // per injector policy
    : public di::config {               | auto injector = di::make_injector<print_types_info_policy>(
public:                                 |     di::bind<i1, impl1>
  auto policies() const noexcept {      | );
    return di::make_policies(           |
      [](auto type                      | injector.create<unique_ptr<i1>>();
       , auto dep                       |
       , auto... ctor) {                | // output:
         using T = decltype(type);      |     0 // ctor_size of impl1
         using arg = typename T::type;  |     unique_ptr<i1> // ctor arg
         using arg_name =               |     di::no_name // ctor arg name
            typename T::name;           |     di::deduce // scope
         using D = decltype(dep);       |     i1 // expected
         using scope =                  |     impl1 // given
            typename D::scope;          |     no_name // dependency
         using expected =               |
            typename D::expected;       |
         using given =                  | // global policy
            typename D::given;          | #define BOOST_DI_CFG my_policy
         using name =                   | auto injector = di::make_injector(
            typename D::name;           |     di::bind<i1, impl1>
         auto ctor_s = sizeof...(ctor); | );
                                        |
         cout << ctor_s                 | injector.create<unique_ptr<i1>>();
              << endl                   |
              << typeid(arg).name()     | // output:
              << endl                   |     0 // ctor_size of impl1
              << typeid(arg_name).name()|     unique_ptr<i1> // cotr arg
              << endl                   |     di::no_name // ctor arg name
              << typeid(scope).name()   |     di::deduce // scope
              << endl                   |     i1 // expected
              << typeid(expected).name()|     impl1 // given
              << endl                   |     no_name // dependency
              << typeid(given).name()   |
              << endl                   |
              << typeid(name).name()    |
              << endl;                  |
         ;                              |
      }                                 |
    );                                  |
  }                                     |
};                                      |
|
`constructible` policy                  | Test
----------------------------------------|-----------------------------------------
#include <boost/di/                     | // global policy
    policies/constructible.hpp>         | #define BOOST_DI_CFG all_must_be_bound_unless_int
                                        | assert(0 == di::make_injector().create<int>());
class all_must_be_bound_unless_int      |
    : public di::config {               | // di::make_injector().create<double>(); // compile error
public:                                 | assert(42.0 == make_injector(
  auto policies() const noexcept {      |                    di::bind<double>.to(42.0)
    using namespace di::policies;       |                ).create<double>()
    using namespace                     | );
        di::policies::operators;        |
                                        |
    return di::make_policies(           |
      constructible(                    |
        is_same<_, int>{} ||            |
        is_bound<_>{})                  |
    );                                  |
  }                                     |
};                                      |
    |

Run-time performance (-O2)

  • Environment
    • x86_64 Intel(R) Core(TM) i7-4770 CPU @ 3.40GHz GenuineIntel GNU/Linux
    • clang++3.4 -O2 / gdb -batch -ex 'file ./a.out' -ex 'disassemble main'
Create type wihtout bindings            | Asm x86-64 (same as `return 0`)
----------------------------------------|-----------------------------------------
int main() {                            | xor %eax,%eax
    auto injector = di::make_injector();| retq
    return injector.create<int>();      |
}                                       |
Create type with bound instance         | Asm x86-64 (same as `return 42`)
----------------------------------------|-----------------------------------------
int main() {                            | mov $0x2a,%eax
    auto injector = di::make_injector(  | retq
        di::bind<int>.to(42)            |
    );                                  |
                                        |
    return injector.create<int>();      |
}                                       |
Create named type                       | Asm x86-64 (same as `return 42`)
----------------------------------------|-----------------------------------------
auto my_int = []{};                     | mov $0x2a,%eax
                                        | retq
struct c {                              |
    BOOST_DI_INJECT(c                   |
        , (named = my_int) int i)       |
        : i(i)                          |
    { }                                 |
                                        |
    int i = 0;                          |
};                                      |
                                        |
int main() {                            |
  auto injector = di::make_injector(    |
    di::bind<int>.named(my_int).to(42)  |
  );                                    |
                                        |
  return injector.create<c>().i;        |
}                                       |
Create bound interface                  | Asm x86-64 (same as `make_unique`)
----------------------------------------|-----------------------------------------
int main() {                            | push   %rax
    auto injector = di::make_injector(  | mov    $0x8,%edi
        di::bind<i1, impl1>             | callq  0x4007b0 <_Znwm@plt>
    );                                  | movq   $0x400a30,(%rax)
                                        | mov    $0x8,%esi
    auto ptr = injector.create<         | mov    %rax,%rdi
        unique_ptr<i1>                  | callq  0x400960 <_ZdlPvm>
    >();                                | mov    $0x1,%eax
                                        | pop    %rdx
    return ptr.get() != nullptr;        | retq
}                                       |
Create bound interface via module       | Asm x86-64 (same as `make_unique`)
----------------------------------------|-----------------------------------------
struct module {                         | push   %rax
	auto configure() const noexcept {   | mov    $0x8,%edi
		return di::make_injector(       | callq  0x4007b0 <_Znwm@plt>
			di::bind<i1, impl1>         | movq   $0x400a10,(%rax)
		);                              | mov    $0x8,%esi
	}                                   | mov    %rax,%rdi
};                                      | callq  0x400960 <_ZdlPvm>
                                        | mov    $0x1,%eax
int main() {                            | pop    %rdx
	auto injector = di::make_injector(  | retq
        module{}                        |
    );                                  |
                                        |
	auto ptr = injector.create<         |
        unique_ptr<i1>                  |
    >();                                |
                                        |
	return ptr != nullptr;              |
}                                       |
Create bound interface via exposed      | Asm x86-64
module                                  | cost = type erasure
----------------------------------------|----------------------------------------------------------------------
struct module {                         | push   %rbx
	di::injector<i1> configure() const {| mov    0x2007e9(%rip),%al
		return di::make_injector(       | test   %al,%al
			di::bind<i1, impl1>         | jne    0x4009be <main+46>
		);                              | mov    $0x601180,%edi
	}                                   | callq  0x4007e0 <__cxa_guard_acquire@plt>
};                                      | test   %eax,%eax
                                        | je     0x4009be <main+46>
int main() {                            | movq   $0x400a00,0x2007b4(%rip
	auto injector = di::make_injector(  | mov    $0x601180,%edi
        module{}                        | callq  0x400820 <__cxa_guard_release@plt>
    );                                  | mov    0x2007a3(%rip),%rax
                                        | mov    $0x601168,%edi
	auto ptr = injector.create<         | callq  *%rax
        unique_ptr<i1>                  | test   %rax,%rax
    >();                                | setne  %cl
                                        | movzbl %cl,%ebx
	return ptr != nullptr;              | je     0x4009e0 <main+80>
}                                       | mov    (%rax),%rcx
                                        | mov    %rax,%rdi
                                        | callq  *0x8(%rcx)
                                        | mov    %ebx,%eax
                                        | pop    %rbx
                                        | retq
                                        | mov    %rax,%rdi
                                        | callq  0x4009f0 <__clang_call_terminate>

Compile-time performance | Example

  • Environment
    • x86_64 Intel(R) Core(TM) i7-4770 CPU @ 3.40GHz GenuineIntel GNU/Linux
    • clang++3.4 -O2
Boost.DI header                         | Time [s]
----------------------------------------|-----------------------------------------
#include <boost/di.hpp>                 | 0.110
int main() { }                          |
Legend:
    ctor    = raw constructor: c(int i, double d);
    inject  = inject constructor: BOOST_DI_INJECT(c, int i, double d);
    all     = all types exposed from module: auto configure();
    exposed = one type exposed from module: di::injector<c> configure();

small complexity

* 4248897537 instances created
* 132 different types
* 10 modules

medium complexity

* 1862039751439806464 instances created
* 200 different types
* 10 modules

big complexity

* 5874638529236910091 instances created
* 310 different types
* 100 different interfaces
* 10 modules

Diagnostic messages

Create interface without bound          | Error message
implementation                          |
----------------------------------------|[clang]--------------------------------------
auto injector = di::make_injector();    | warning: 'create' is deprecated: creatable constraint not satisfied
injector.create<i*>();                  |     injector.create<i*>();
                                        |              ^
                                        | note: 'create<i *, 0>' has been explicitly marked deprecated here
                                        |     T create() const {
                                        |       ^
                                        | error: inline function 'boost::di::abstract_type<i>::is_not_bound::error' is not defined
                                        |     error(_ = "type not bound, did you forget to add: 'di::bind<interface, implementation>'?")
                                        |     ^
                                        | note: used here
                                        |     constraint_not_satisfied{}.error();
                                        |
                                        |[gcc]----------------------------------------
                                        | error: inline function ‘constexpr T* boost::di::abstract_type<T>::is_not_bound::error(boost::di::_) const [with T = i]’ used but never defined
                                        |      error(_ = "type not bound, did you forget to add: 'di::bind<interface, implementation>'?")
                                        |      ^
                                        | error: call to ‘boost::di::core::injector<boost::di::config>::create<i*, 0>’ declared with attribute error: creatable constraint not satisfied
                                        |      injector.create<i*>();
Ambiguous binding                       | Error message
----------------------------------------|-----------------------------------------
auto injector = di::make_injector(      | error: base class 'pair<int, no_name>'
    di::bind<int>.to(42)                | specified more than once as a direct
  , di::bind<int>.to(87)                | base class
);                                      |
                                        |
injector.create<int>();                 |
Create not bound object with all bound  | Error message
policy                                  |
----------------------------------------|-----------------------------------------
class all_bound : public di::config {   | error: static_assert failed
public:                                 | "Type T is not allowed"
  auto policies() const noexcept {      |
    return di::make_policies(           |
      constructible(is_bound<_>{})      |
    );                                  |
  }                                     |
};                                      |
                                        |
auto injector =                         |
    di::make_injector<all_bound>();     |
                                        |
injector.create<int>();                 |
Wrong annotation                        | Error message
(NAMED instead of named)                |
----------------------------------------|-----------------------------------------
auto name = []{};                       | error: use of undeclared identifier
                                        | 'named'
struct c {                              |
    BOOST_DI_INJECT(c                   |
        , (NAMED = name) int) { }       |
};                                      |
                                        |
di::make_injector().create<c>();        |

Configuration

Macro                                   | Description
----------------------------------------|-----------------------------------------
BOOST_DI_CFG_CTOR_LIMIT_SIZE            | Limits number of allowed consturctor
                                        | parameters [0-10, default=10]
----------------------------------------|-----------------------------------------
BOOST_DI_CFG                            | Global configuration allows to customize
                                        | provider and policies

Contributing

  • Extensions (e.g. see extensions)
  • Core (for example. bug fixes / improvements of compilation/run-time times)

Stories in Progress Throughput Graph


Disclaimer Boost.DI is not an official Boost library.

About

C++ Dependency Injection

http://krzysztof-jusiak.github.io/di/cpp14/boost/libs/di/doc/html


Languages

Language:C++ 98.8%Language:CMake 1.1%Language:HTML 0.1%