Boost.DI: впровадження залежності в С++

«Не телефонуй нам. Ми зателефонуємо тобі самі.» — принцип Голлівуду

Впровадження залежності (Dependency Injection — DI) означає передачу (впровадження) однієї чи більше залежно якого-небудь об'єкту (клієнту, компоненту) таким чином, що після впровадження ця залежність стає частиною стану об'єкта. Це трохи нагадує шаблон проектування Стратегія, з тією лише різницею, що стратегія задається лише одного разу — в конструкторі. DI дозволяє створювати більш слабко пов'язану архітектуру програми, яка краще піддається підтримки і тестування.

Отже, ще раз плюси:
  • Зменшує зв'язок компонент (відділяє бізнес-логіку від створення об'єкта)
  • Дозволяє писати більш підтримуваний код (в одні і ті ж об'єкти ми легко можемо впроваджувати різні залежності)
  • Дозволяє писати більш досліджуваний код (ми можемо легше використовувати стаби і моки)


Без впровадження залежності З впровадженням залежності
class example { 
public: 
example() 
: logic_(new logic{}) 
, logger_( 
logger_factory::create() 
) 
{ } 

int run() const; 

private: 
shared_ptr<ilogic> logic_; 
shared_ptr<ilogger> logger_; 
}; 

class example {
public:
example(shared_ptr<ilogic> logic
, shared_ptr<ilogger> logger)
: logic_(logic), logger_(logger)
{ }

int run() const;

private:
shared_ptr<ilogic> logic_;
shared_ptr<ilogger> logger_;
};


Boost.DI це бібліотека для реалізації впровадження залежності в С++. Для її використання потрібно лише включити в свій код один заголовковий файл. Мета бібліотеки — спростити створення об'єктів за допомогою автоматичного впровадження залежностей:
  • Зменшення кількості «вузьких місць» у коді — ніяких зайвих фабрик, ніякого створення об'єктів лише у певному порядку
  • Спрощення підтримки коду — зміна сигнатури конструктора не вплине на конфігурацію DI
  • Спрощення тестування — автоматична ін'єкція моков
  • Більш хороший контроль над тим, що і коли створюється
  • Краще розуміння коду в плані ієрархії об'єктів


Ручне впровадження залежності Boost.DI
int main() { 
/*boilerplate code*/ 
auto logic = make_shared<logic>(); 
auto logger = make_shared<logger>();

return example{logic, logger}.run();
} 

int main() {
auto injector = di::make_injector(
di::bind<ilogic, logic>
, di::bind<ilogger, logger>
);
return injector.create<example>().run();
}


Ще раз — навіщо використовувати впровадження залежності



image



З чого почати?



  • Візьміть компілятор з підтримкою С++14 (Clang-3.4+ GCC-5.0+). Бібліотека Boost не потрібно
  • Прочитайте цей документ
  • Прочитайте підручник
  • Прочитайте документацію


А взагалі-то все, що вам необхідно для роботи з бібліотекою, цієї файл di.hpp

// main.cpp
#include "di.hpp"
int main() { }


$CXX-std=c++1y-I. main.cpp


Давайте припустимо, що всі приклади нижче включають boost/di.hpp і визначають простір імен di як аліас boost::di.

#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 { } };


Інжектор

Створення порожнього інжектора Тест
auto injector = di::make_injector(); 

assert(0 == injector.create<int>()); 

Прив'язки

Приклади
Ще приклади
Прив'язка інтерфейсу до реалізації Тест
auto injector = di::make_injector(di::bind<i1, impl1>);

auto object = injector.create<unique_ptr<i1>>(); 
assert(dynamic_cast<impl1*>(object.get()));

Прив'язка декількох інтерфейсів до однієї реалізації Тест
auto injector = di::make_injector(di::bind<di::any_of<i1, i2>, impl>); 

auto object1 = injector.create<shared_ptr<i1>>(); 
auto object2 = injector.create<shared_ptr<i2>>();
assert(dynamic_cast<impl*>(object1.get()));
assert(dynamic_cast<impl*>(object2.get()));

Прив'язка типу до значення, обчислюваного на етапі компіляції Тест
template < int N> using int_ = integral_constant<int, N>; 
auto injector = di::make_injector(di::bind<int, int_<42>>);

assert(42 == injector.create<int>()); 

Прив'язка типу до значення Тест
auto injector = di::make_injector(di::bind<int>.to(42));

assert(42 == injector.create<int>()); 

Впровадження

Приклади
Ще приклади
Впровадження через звичайний конструктор Тест
struct c { 
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) 
);

auto object = injector.create<c>();
assert(42 == object.a);
assert(87.0 == object.d);

Впровадження через аггрегацию Тест
struct c { 
int a = 0; 
double d = 0.0; 
}; 

auto injector = di::make_injector(
di::bind<int>.to(42) 
, di::bind<double>.to(87.0) 
); 

auto object = injector.create<c>();
assert(42 == object.a);
assert(87.0 == object.d);

Впровадження через конструктор при наявності
кількох конструкторів (буде обраний той, у якого
найбільше число параметрів)
Тест
struct c { 
c(); 
c(int a) : a(a) { } 
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) 
);

auto object = injector.create<c>(); 
assert(42 == object.a);
assert(87.0 == object.d);

Впровадження через конструктор при наявності варіантів
вибору (використання BOOST_DI_INJECT)
Тест
struct c { 
c(double d, int a) : a(a), d(d) { }
BOOST_DI_INJECT(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) 
);

auto object = injector.create<c>(); 
assert(42 == object.a);
assert(87.0 == object.d);

Впровадження через конструктор при наявності варіантів
вибору (використання BOOST_DI_INJECT_TRAITS)
Тест
struct c { 
BOOST_DI_INJECT_TRAITS(int, double);
c(double d, int a) : a(a), d(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) 
);

auto object = injector.create<c>(); 
assert(42 == object.a);
assert(87.0 == object.d);

Впровадження через конструктор при наявності варіантів
вибору (використання di::ctor_traits)
Тест
struct c { 
c(double d, int a) : a(a), d(d) { } 
c(int a, double d) : a(a), d(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) 
); 

auto object = injector.create<c>(); 
assert(42 == object.a);
assert(87.0 == object.d);

Анотації

Приклади
Впровадження через анотовані конструктори Тест
auto int1 = []{}; 
auto int2 = []{}; 

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)
); 

auto object = injector.create<c>();
assert(42 == object.a);
assert(87 == object.b);

Впровадження через анотовані конструктори з
використанням іменованих параметрів
Тест
auto n1 = []{}; 
auto n2 = []{}; 

struct c { 
BOOST_DI_INJECT(c 
, (named = n1) int a 
, (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")
); 

auto object = injector.create<c>(); 
assert(42 == object.i1);
assert(42 == object.i2);
assert(87 == object.i3);
assert(0 == object.i4);
assert("str" == c.s);

Впровадження через анотовані конструктори з
винесенням реалізації конструктора
Тест
auto int1 = []{}; 
auto int2 = []{}; 

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)
); 

auto object = injector.create<c>(); 
assert(42 == object.a);
assert(87 == object.b);

Впровадження через анотовані конструктори з
використанням di::ctor_traits
Тест
auto int1 = []{}; 
auto int2 = []{}; 

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)
); 

auto object = injector.create<c>(); 
assert(42 == object.a);
assert(87 == object.b);

Області видимості

Приклади
Ще приклади

Висновок області видимості (за замовчуванням) Тест
struct c { 
shared_ptr<i1> sp; /*singleton*/
unique_ptr<i2> up; /*unique*/ 
int& i; /*external*/ 
double d; /*unique*/ 
}; 

auto i = 42; 

auto injector = di::make_injector( 
di::bind<i1, impl1> 
, di::bind<i2, impl2> 
, di::bind<int>.to(ref(i)) 
, di::bind<double>.to(87.0) 
); 

auto object1 = injector.create<unique_ptr<c>>();
auto object2 = injector.create<unique_ptr<c>>();
assert(object1->sp == object2->sp);
assert(object1->up != object2->up);
assert(42 == object1->i);
assert(&i == &object1->i;
assert(42 == object2->i);
assert(&i == &object2->i);
assert(87.0 == object1->d);
assert(87.0 == object2->d);

Тип Виведена область видимості
T unique
T& помилка — має бути пов'язаний як external
const T& unique (тимчасовий)
T* unique (передача володіння)
const T* unique (передача володіння)
T&& unique
unique_ptr unique
shared_ptr singleton
weak_ptr singleton
Унікальна область видимості Тест
auto injector = di::make_injector( 
di::bind<i1, impl1>.in(di::unique)
); 

assert(injector.create<shared_ptr<i1>>()
!=
injector.create<shared_ptr<i1>>()
| );

Спільна область видимості (в межах потоку) Тест
auto injector = di::make_injector( 
di::bind<i1, impl1>.in(di::shared)
); 

assert(injector.create<shared_ptr<i1>>()
==
injector.create<shared_ptr<i1>>()
| );

Сінглтон (поділяємо між потоками) Тест
auto injector = di::make_injector( 
di::bind<i1, impl1>.in(di::singleton)
); 

assert(injector.create<shared_ptr<i1>>()
==
injector.create<shared_ptr<i1>>());

Область видимості сесії Тест
auto my_session = []{}; 

auto injector = di::make_injector(
di::bind<i1, impl1>.in( 
di::session(my_session) 
) 
); 

assert(nullptr == injector.create<shared_ptr<i1>>());
injector.call(di::session_entry(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>>());

Зовнішня область видимості Тест
auto l = 42l; 
auto b = false; 

auto injector = di::make_injector( 
di::bind<int, int_<41>> 
, di::bind<int>.to(42) 
, di::bind<i1>.to(make_shared<impl>())
, di::bind<long>.to(ref(l)) 
, di::bind<short>.to([]{return 87;}) 
, di::bind<i2>.to( 
[&](const auto& injector) 
-> shared_ptr<i2> { 
if (b) { 
return injector.template 
create< 
shared_ptr<impl2>>();
} 
return nullptr; 
} 
) 
); 

assert(42 == injector.create<int>()); // external has priority
assert(injector.create<shared_ptr<i1>>()
==
injector.create<shared_ptr<i1>>()
);
assert(l == injector.create<long&>());
assert(&l == &injector.create<long&>());
assert(87 == injector.create<short>());
{
auto object = injector.create<shared_ptr<i2>>();
assert(nullptr == object);
}
{
b = true;
auto object = injector.create<shared_ptr<i2>>();
assert(dynamic_cast<impl2*>(object.get()));
} 

Спеціальна область видимості Тест
struct custom_scope { 
static constexpr 
auto priority = false; 

template < class TExpected, class> 
struct scope { 
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{})
); 

assert(injector.create<shared_ptr<i1>>()
!=
injector.create<shared_ptr<i1>>()
);



Модулі

Приклади
Модуль Тест
struct c { 
c(unique_ptr<i1> i1 
, unique_ptr<i2> i2 
, int i) : i1(move(i1)) 
, i2(move(i2)), i(i) 
{ } 

unique_ptr<i1> i1; 
unique_ptr<i2> i2; 
int i = 0; 
}; 

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{} 
); 

auto object = injector.create<unique_ptr<c>>();
assert(dynamic_cast<impl1*>(object->i1.get()));
assert(dynamic_cast<impl2*>(object->i2.get()));
assert(42 == object->i);
auto up1 = injector.create<unique_ptr<i1>>();
assert(dynamic_cast<impl1*>(up1.get()));

auto up2 = injector.create<unique_ptr<i2>>();
assert(dynamic_cast<impl2*>(up2.get()));

Модуль, що відкриває тип Тест
struct c { 
c(shared_ptr<i1> i1 
, shared_ptr<i2> i2 
, int i) : i1(i1), i2(i2), i(i) 
{ } 

shared_ptr<i1> i1; 
shared_ptr<i2> i2; 
int i = 0; 
}; 

struct module { 
di::injector<c> configure() 
const noexcept; 

int i = 0; 
}; 

di::injector<c> // відкриває 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} 
);

auto object = injector.create<c>();
assert(dynamic_cast<impl1*>(object.i1.get()));
assert(dynamic_cast<impl2*>(object.i2.get()));
assert(42 == object.i);

// injector.create<unique_ptr<i1>>() // помилка компіляції
// injector.create<unique_ptr<i2>>() // помилка компіляції

Модуль, відкриває кілька типів Тест
struct module { 
di::injector<i1, i2> configure() 
const noexcept; 

int i = 0; 
}; 

di::injector<i1, i2> // відкриває i1, i2
module::configure() const noexcept { 
return di::make_injector( 
di::bind<i1, impl1> 
, di::bind<i2, impl2> 
); 
} 

auto injector = di::make_injector( 
module{} 
); 

auto up1 = injector.create<unique_ptr<i1>>();
assert(dynamic_cast<impl1*>(up1.get()));

auto up2 = injector.create<unique_ptr<i2>>();
assert(dynamic_cast<impl2*>(up2.get()));

Модуль відкритого типу з анотацією Тест
auto my = []{}; 

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) 
); 

auto object = injector.create<unique_ptr<c>>();
assert(dynamic_cast<impl1*>(object->up.get()));

Провайдери

Приклади
«no throw»-провайдер Тест
class heap_no_throw { 
public: 
template< 
class // interface 
, class T // implementation 
, class TInit // direct()/uniform{} 
, class TMemory // heap/stack 
, class... TArgs> 
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{}; 
} 
}; 

// політика інжектора
auto injector = di::make_injector<my_provider>();
assert(0 == injector.create<int>());
// глобальна політика
#define BOOST_DI_CFG my_provider
auto injector = di::make_injector();
assert(0 == injector.create<int>());

Політики

Приклади
Ще приклади
Визначення політик конфігурації (дамп типів) Тест
class print_types_policy 
: public di::config { 
public: 
auto policies() const noexcept { 
return di::make_policies( 
[](auto type){ 
using T = decltype(type); 
using arg = typename T:type;
cout << typeid(arg).name() 
<< endl; 
} 
); 
} 
}; 

// політика інжектора
auto injector = di::make_injector<print_types_policy>();
injector.create<int>(); // висновок: int
// глобальна політика
#define BOOST_DI_CFG my_policy
auto injector = di::make_injector();
injector.create<int>(); // висновок: int

Визначення політик конфігурації (розгорнутий дамп типів) Тест
class print_types_info_policy 
: public di::config { 
public: 
auto policies() const noexcept { 
return di::make_policies( 
[](auto type 
, auto dep 
, auto... ctor) { 
using T = decltype(type); 
using arg = typename T:type; 
using arg_name = 
typename T::name; 
using D = decltype(dep); 
using scope = 
typename D::scope; 
using expected = 
typename D::expected; 
using given = 
typename D::given; 
using name = 
typename D::name; 
auto ctor_s = sizeof...(ctor); 

cout << ctor_s 
<< endl 
<< typeid(arg).name() 
<< endl 
<< typeid(arg_name).name()
<< endl 
<< typeid(scope).name() 
<< endl 
<< typeid(expected).name()
<< endl 
<< typeid(given).name() 
<< endl 
<< typeid(name).name() 
<< endl; 
; 
} 
); 
} 
}; 

// політика інжектора
auto injector = di::make_injector<print_types_info_policy>(
di::bind<i1, impl1>
);
injector.create<unique_ptr<i1>>();
// висновок:
0 // ctor_size of impl1
unique_ptr<i1> // ctor arg
di::galicia // ctor arg name
di::deduce // scope
i1 // expected
impl1 // given
no_name // dependency
// глобальна політика
#define BOOST_DI_CFG my_policy
auto injector = di::make_injector(
di::bind<i1, impl1>
);
injector.create<unique_ptr<i1>>();
// висновок:
0 // ctor_size of impl1
unique_ptr<i1> // cotr arg
di::galicia // ctor arg name
di::deduce // scope
i1 // expected
impl1 // given
no_name // dependency

Політика «може бути сконструйований» Тест
#include <boost/di/ 
policies/конструктивні.hpp> 

class all_must_be_bound_unless_int
: public di::config { 
public: 
auto policies() const noexcept {
using namespace di::policies; 
using namespace 
di::policies::operators; 

return di::make_policies( 
конструктивні( 
is_same<_, int>{} || 
is_bound<_>{}) 
); 
} 
}; 

// глобальна політика
#define BOOST_DI_CFG all_must_be_bound_unless_int
assert(0 == di::make_injector().create<int>());
// di::make_injector().create<double>(); // помилка компіляції
assert(42.0 == make_injector(
di::bind<double>.to(42.0)
).create<double>()
);

Продуктивність на рантайме

Оточення
  • x86_64 Intel® Core(TM) i7-4770 CPU @ 3.40 GHz GenuineIntel GNU/Linux
  • clang++3.4-O2 / gdb-batch-ex 'file ./a.out' -ex 'disassemble main'


Створення типу без прив'язок Asm x86-64 (те ж, що «return 0»)
int main() { 
auto injector = di::make_injector();
return injector.create<int>(); 
} 

xor %eax,%eax
retq

Створення типу з прив'язкою об'єкта Asm x86-64 (те ж, що «return 42»)
int main() { 
auto injector = di::make_injector(
di::bind<int>.to(42) 
); 

return injector.create<int>(); 
} 

mov $0x2a,%eax
retq

Створення іменованого типу Asm x86-64 (те ж, що «return 42»)
auto my_int = []{}; 

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; 
} 

mov $0x2a,%eax
retq

Створення прив'язки інтерфейсу до реалізації Asm x86-64 (те ж, що «make_unique»)
int main() { 
auto injector = di::make_injector(
di::bind<i1, impl1> 
); 

auto ptr = injector.create< 
unique_ptr<i1> 
>(); 

return ptr.get() != nullptr; 
} 

push %rax
mov $0x8,%edi
callq 0x4007b0 <_Znwm@plt>
movq $0x400a30,(%rax)
mov $0x8,%esi
mov %rax,%rdi
callq 0x400960 <_ZdlPvm>
mov $0x1,%eax
pop %rdx
retq

Створення прив'язки інтерфейсу через модуль Asm x86-64 (те ж, що «make_unique»)
struct module { 
auto configure() const noexcept { 
return di::make_injector( 
di::bind<i1, impl1> 
); 
} 
}; 

int main() { 
auto injector = di::make_injector(
module{} 
); 

auto ptr = injector.create< 
unique_ptr<i1> 
>(); 

return ptr != nullptr; 
}

push %rax
mov $0x8,%edi
callq 0x4007b0 <_Znwm@plt>
movq $0x400a10,(%rax)
mov $0x8,%esi
mov %rax,%rdi
callq 0x400960 <_ZdlPvm>
mov $0x1,%eax
pop %rdx
retq

Створення прив'язки інтерфейсу через відкритий модуль Asm x86-64
ціна = виклик віртуального методу
struct module { 
di::injector<i1> configure() const {
return di::make_injector( 
di::bind<i1, impl1> 
); 
} 
}; 

int main() { 
auto injector = di::make_injector( 
module{} 
); 

auto ptr = injector.create< 
unique_ptr<i1> 
>(); 

return ptr != nullptr; 
} 

push %rbp mov (%rax),%ecx
push %rbx lea-0x1(%rcx),%edx
sub $0x38,%rsp mov %edx,(%rax)
lea 0x10(%rsp),%rdi cmp $0x1,%ecx
lea 0x8(%rsp),%rsi jne 0x400bcd <main+173>
callq 0x400bf0 <_ZN5boost2di7exposed> mov (%rbx),%rax
mov 0x18(%rsp),%rdi mov %rbx,%rdi
mov (%rdi),%rax callq *0x10(%rax)
lea 0x30(%rsp),%rsi lea 0xc(%rbx),%rax
callq *0x10(%rax) mov $0x0%ecx
test %rax,%rax test %rcx,%rcx
setne %bpl je 0x400bb8 <main+152>
je 0x400b57 <main+55> mov $0xffffffff,%ecx
mov (%rax),%rcx lock xadd %ecx,(%rax)
mov %rax,%rdi mov %ecx,0x30(%rsp)
callq *0x8(%rcx) mov 0x30(%rsp),%ecx
mov 0x20(%rsp),%rbx jmp 0x400bbf <main+159>
test %rbx,%rbx mov (%rax),%ecx
je 0x400bcd <main+173> lea-0x1(%rcx),%edx
lea 0x8(%rbx),%rax mov %edx,(%rax)
mov $0x0%ecx cmp $0x1,%ecx
test %rcx,%rcx jne 0x400bcd <main+173>
je 0x400b82 <main+98> mov (%rbx),%rax
mov $0xffffffff,%mov ecx %rbx,%rdi
lock xadd %ecx,(%rax) callq *0x18(%rax)
mov %ecx,0x30(%rsp) movzbl %bpl,%eax
mov 0x30(%rsp),%ecx add $0x38,%rsp
jmp 0x400b89 <main+105> pop %rbx
pop %rbp
--> retq

Продуктивність на етапі компіляції

Приклад

Оточення
  • x86_64 Intel® Core(TM) i7-4770 CPU @ 3.40 GHz GenuineIntel GNU/Linux
  • clang++3.4-O2


Заголовковий файл Boost.DI Час [с]
#include <boost/di.hpp> 
int main() { } 

0.165
Легенда
  • ctor = «голий» конструктор: c(int i, double d);
  • inject = впровадження в конструктор: BOOST_DI_INJECT(c, int i, double d);
  • all = всі типи у модулі доступні: auto configure();
  • exposed = один тип в модулі доступний: di::injector<c> configure();



  • 4248897537 об'єктів створено
  • 132 різних типу
  • 10 модулів



  • 1862039751439806464 об'єкта створено
  • 200 різних типів
  • 10 модулів



  • 5874638529236910091 об'єкт створений
  • 310 різних типів
  • 100 різних інтерфейсів
  • 10 модулів


Діагностичні повідомлення

Створення інтерфейсу без прив'язки до реалізації Повідомлення про помилку
auto injector = di::make_injector();
injector.create<unique_ptr<i1>>(); 

error: allocating an object of abstract 
class type 'i1' return new (nothrow)
T{forward<TArgs>(args)...};

Неоднозначність прив'язки Повідомлення про помилку
auto injector = di::make_injector(
di::bind<int>.to(42) 
, di::bind<int>.to(87) 
);
injector.create<int>(); 

error: base class 'pair<int, no_name>'
specified more than once as a direct
base class

Створення об'єкта без прив'язок
при політиці, що вимагає їх
Повідомлення про помилку
class all_bound : public di::config {
public: 
auto policies() const noexcept {
return di::make_policies(
конструктивні(is_bound<_>{}) 
);
} 
};

auto injector = di::make_injector<all_bound>();
injector.create<int>();

error: failed static_assert 
"Type T is not allowed"

Невірний синтаксис анотацій
(NAMED замість named)
Повідомлення про помилку
auto name = []{}; 

struct c { 
BOOST_DI_INJECT(c 
, (NAMED = name) int) { }
}; 

di::make_injector().create<c>(); 

error: use of undeclared identifier
'named'

Налаштування

Макрос Опис
BOOST_DI_CFG_CTOR_LIMIT_SIZE 

----------------------------------------
BOOST_DI_CFG 

----------------------------------------
BOOST_DI_INJECTOR 


Ліміт на максимальну кількість дозволених 
параметрів конструктора [0-10, за замовчуванням 10]
----------------------------------------
Глобальна конфігурація, що дозволяє 
перевизначати провайдери і політики
----------------------------------------
Ім'я, що використовується всередині Boost.DI для визначення 
властивостей конструкторів
[за замовчуванням boost_di_injector__]

Схожі бібліотеки



Ліцензія: Boost Software License, Версія 1.0
Нюанс: бібліотека поки що не входить в Boost, а лише знаходиться в "інкубаторі".

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.