Is there any interest in a dependency injection library?

Hi,
Is there any interest in a C++03/C++11/C++14 header only, type safe,
library providing compile time, macro free *constructor dependency
injection http://en.wikipedia.org/wiki/Dependency_injection*?
*Key features:*
* *Compile time *- no exceptions - if application compiles all dependencies
will be be created accurately
* *Macro free* - by default no need to specify constructor traits or
register anything (less intrusive)
* *Scopes deduction* - scopes are deduced based on type semantic
* *Automatic conversion* between std/boost smart pointers
* *Compile time policies* - example to detect circular dependencies or
limit supported types only to specified
*Hello World*
---------------------------------------------------------------------------------------------------------->
#include <memory>
#include

On 2014-07-24, 3:47 PM, Krzysztof Jusiak wrote:
Hi,
Is there any interest in a C++03/C++11/C++14 header only, type safe, library providing compile time, macro free *constructor dependency injection http://en.wikipedia.org/wiki/Dependency_injection*?
Hi Krzysztof, Definitely interested and will send more comments and questions in a few days. Immediately though: is it possible to separately compile modules and/or the configuration? From what I can tell, the injector is all compile-time, which is actually really great *but* it seems like it requires you to #include everything in one place which can wreak havoc on productivity, ironically. For example, I have ~800 lines of binding code, factored into different files and recompiling all those headers + the template-heavy binding code every time I change one header somewhere else is pointless. You linked to my library and one of the insights you have made in yours is that 99.999999999% of the time, the types are actually decided at compile-time whereas in mine, it is always at runtime (yuck). So good job on that. Thanks for doing this, now I don't have to! :) Sohail

Hi Sohail,
*>Definitely interested and will send more comments and questions in a few
days*
Thank you for the interest in the library and looking forward for your
comments and questions.
*>Immediately though: is it possible to separately compile modules and/or
the configuration? *
Yea, it is possible, IMHO the easiest way will be to take advantage of type
erasure here, but Pimpl idiom might be - with a bit of effort - achieved as
well.
So, let's say we want to create application - app - using DI and
configuration specified in module - cpp file.
###app.hpp
class app {
public:
app(std::shared_ptr<ilogic>, std::shared_ptr<ilogger>);
int run();
private:
std::shared_ptr<ilogic> logic_;
std::shared_ptr<ilogger> logger_;
};
To achieve that with type erasure we are going to declare dynamic_injector
which will be able to create app for us.
###dynamic_injector.hpp
template<typename T>
class dynamic_injector {
public:
template<typename TInjector>
dynamic_injector(const TInjector& injector)
: injector_(new TInjector(injector))
, f_([&]{ return static_cast
On 2014-07-24, 3:47 PM, Krzysztof Jusiak wrote:
Hi,
Is there any interest in a C++03/C++11/C++14 header only, type safe, library providing compile time, macro free *constructor dependency injection http://en.wikipedia.org/wiki/Dependency_injection*?
Hi Krzysztof,
Definitely interested and will send more comments and questions in a few days. Immediately though: is it possible to separately compile modules and/or the configuration? From what I can tell, the injector is all compile-time, which is actually really great *but* it seems like it requires you to #include everything in one place which can wreak havoc on productivity, ironically. For example, I have ~800 lines of binding code, factored into different files and recompiling all those headers + the template-heavy binding code every time I change one header somewhere else is pointless.
You linked to my library and one of the insights you have made in yours is that 99.999999999% of the time, the types are actually decided at compile-time whereas in mine, it is always at runtime (yuck). So good job on that.
Thanks for doing this, now I don't have to! :)
Sohail
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/ mailman/listinfo.cgi/boost

Hi Sohail,
Definitely interested and will send more comments and questions in a few days Thank you for the interest in the library and looking forward for your comments and questions.
Immediately though: is it possible to separately compile modules and/or the configuration? Yea, it is possible, IMHO the easiest way will be to take advantage of type erasure here, but Pimpl idiom might be - with a bit of effort - achieved as well. So, let's say we want to create application - app - using DI and configuration specified in module - cpp file.
You linked to my library and one of the insights you have made in yours is
###app.hpp
class app {
public:
app(std::shared_ptr<ilogic>, std::shared_ptr<ilogger>);
int run();
private:
std::shared_ptr<ilogic> logic_;
std::shared_ptr<ilogger> logger_;
};
To achieve that with type erasure we are going to declare dynamic_injector
which will be able to create app for us.
###dynamic_injector.hpp
template<typename T>
class dynamic_injector {
public:
template<typename TInjector>
dynamic_injector(const TInjector& injector)
: injector_(new TInjector(injector))
, f_([&]{ return static_cast
(yuck). So good job on that. Thank you, that was one of my main goals whilst designing the library - "Prefer compile- and link-time errors to run-time errors".
Kris
On Fri, Jul 25, 2014 at 1:58 PM, Sohail Somani
On 2014-07-24, 3:47 PM, Krzysztof Jusiak wrote:
Hi,
Is there any interest in a C++03/C++11/C++14 header only, type safe, library providing compile time, macro free *constructor dependency injection http://en.wikipedia.org/wiki/Dependency_injection*?
Hi Krzysztof,
Definitely interested and will send more comments and questions in a few days. Immediately though: is it possible to separately compile modules and/or the configuration? From what I can tell, the injector is all compile-time, which is actually really great *but* it seems like it requires you to #include everything in one place which can wreak havoc on productivity, ironically. For example, I have ~800 lines of binding code, factored into different files and recompiling all those headers + the template-heavy binding code every time I change one header somewhere else is pointless.
You linked to my library and one of the insights you have made in yours is that 99.999999999% of the time, the types are actually decided at compile-time whereas in mine, it is always at runtime (yuck). So good job on that.
Thanks for doing this, now I don't have to! :)
Sohail
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/ mailman/listinfo.cgi/boost

Hi Sohail,
Definitely interested and will send more comments and questions in a few days Thank you for the interest in the library and looking forward for your comments and questions.
Immediately though: is it possible to separately compile modules and/or the configuration? Yea, it is possible, IMHO the easiest way will be to take advantage of type erasure here, but Pimpl idiom might be - with a bit of effort - achieved as well. So, let's say we want to create application - app - using DI and configuration specified in module - cpp file.
You linked to my library and one of the insights you have made in yours is
###app.hpp
class app {
public:
app(std::shared_ptr<ilogic>, std::shared_ptr<ilogger>);
int run();
private:
std::shared_ptr<ilogic> logic_;
std::shared_ptr<ilogger> logger_;
};
To achieve that with type erasure we are going to declare dynamic_injector
which will be able to create app for us.
###dynamic_injector.hpp
template<typename T>
class dynamic_injector {
public:
template<typename TInjector>
dynamic_injector(const TInjector& injector)
: injector_(new TInjector(injector))
, f_([&]{ return static_cast
(yuck). So good job on that. Thank you, that was one of my main goals whilst designing the library - "Prefer compile- and link-time errors to run-time errors".
Kris -- View this message in context: http://boost.2283326.n4.nabble.com/Is-there-any-interest-in-a-dependency-inj... Sent from the Boost - Dev mailing list archive at Nabble.com.

On 2014-07-25, 4:01 PM, Kris wrote:
Hi Sohail,
Definitely interested and will send more comments and questions in a few days Thank you for the interest in the library and looking forward for your comments and questions.
Immediately though: is it possible to separately compile modules and/or the configuration? Yea, it is possible, IMHO the easiest way will be to take advantage of type erasure here, but Pimpl idiom might be - with a bit of effort - achieved as well. So, let's say we want to create application - app - using DI and configuration specified in module - cpp file.
[snip example] So that example is fine when it comes to a single type, but I have hundreds of types that are separately compiled. Creating an injector for each type is cumbersome. Is there a way you can come up with a solution that is generic? I know you suggested type lists but that is too much of a burden.
You linked to my library and one of the insights you have made in yours is that 99.999999999% of the time, the types are actually decided at compile-time whereas in mine, it is always at runtime > (yuck). So good job on that. Thank you, that was one of my main goals whilst designing the library - "Prefer compile- and link-time errors to run-time errors".
In my library, there was only one case I cared about that may not be caught at compile-time: the (lack of) concrete implementation in a base class. I haven't bothered to fix it because the error happens immediately at runtime and the exception thrown tells you what to do. Other than that, I don't recall a single run-time issue beyond this (threading issues aside), but what I was referring to regarding your compile-time bit was that the way you've written it, you can demand an implementation for a base class, which is pretty cool to me. If there was some way to marry your static checking with the convenience of separately compiling the bindings without having to do it for each individual type, I'd switch to your library in a heartbeat! Sohail

Hi Sohail,
So that example is fine when it comes to a single type, but I have hundreds of types that are separately compiled. Creating an injector for each type is cumbersome. Is there a way you can come up with a solution that is generic? I know you suggested type lists but that is too much of a burden.
Okay, the example was limited to one type, but to the - only - type which
should be created if composition root (the only call of
create/construct/getInstance/...) would have been applied. Otherwise,
agreed, example was limited to one type and type list could have been a bit
hard to use after all as well.
Nevertheless I see few solutions to types which are separately compiled:
1) if composition root is applied, it shouldn't be a problem with the
solution shown in the thread already
2) usage of forward declarations seems to be working fine with modules -
example with separately compiled modules and forward decelerated types in
header files might be found in:
http://krzysztof-jusiak.github.io/di/boost/libs/di/doc/html/di/examples/more...
So, in the example, the basic idea is to forward declaration needed types in
configuration header file and use them in cpp file.
#module.hpp
class implementation;
class module {
using injector = di::injectordi::deduce<implementation>;
public:
injector configure() const;
};
#module.cpp
#include "module.hpp"
#include "implementation.hpp"
module::injector module::configure() const {
return di::injectordi::deduce<implementation>();
}
3) mixed runtime/compile time solution, which, at some point, I made
proof-of-concept of, but I did give up on in the end, due to the fact I
wasn't able to get compile time checking working in many cases, runtime
overhead and implementation complexity didn't help either - anyway such
solution might be easily implemented on top of current library,
proof-of-concept and the idea might be found in:
https://github.com/krzysztof-jusiak/di_runtime_injector
di::injector<> injector;
injector.install(
di::bind
In my library, there was only one case I cared about that may not be caught at compile-time: the (lack of) concrete implementation in a base class. I haven't bothered to fix it because the error happens immediately at runtime and the exception thrown tells you what to do.
Yea, but IMHO it depends on the project scale and tests coverage tho, a lot of projects are so huge and so badly tested that runtime exception may happen in production either way. Besides that compile time checking give so much opportunities like limiting allowed types, checking for circular dependencies, etc...
Other than that, I don't recall a single run-time issue beyond this (threading issues aside), but what I was referring to regarding your compile-time bit was that the way you've written it, you can demand an implementation for a base class, which is pretty cool to me. If there was some way to marry your static checking with the convenience of separately compiling the bindings without having to do it for each individual type, I'd switch to your library in a heartbeat!
Thanks:) Anyway IMHO runtime binding may have some abilities of static checking, but exceptions will have to be thrown there either way. Separately compiling bindings definitely might be achieved with static checking for example with usage of forward declarations or composition root approach. Thanks for your feedback, Kris -- View this message in context: http://boost.2283326.n4.nabble.com/Is-there-any-interest-in-a-dependency-inj... Sent from the Boost - Dev mailing list archive at Nabble.com.

On 2014-07-27, 5:46 PM, Kris wrote:
Hi Sohail,
So that example is fine when it comes to a single type, but I have hundreds of types that are separately compiled. Creating an injector for each type is cumbersome. Is there a way you can come up with a solution that is generic? I know you suggested type lists but that is too much of a burden.
[snip]
3) mixed runtime/compile time solution, which, at some point, I made proof-of-concept of, but I did give up on in the end, due to the fact I wasn't able to get compile time checking working in many cases, runtime overhead and implementation complexity didn't help either - anyway such solution might be easily implemented on top of current library, proof-of-concept and the idea might be found in: https://github.com/krzysztof-jusiak/di_runtime_injector
di::injector<> injector; injector.install( di::bind
() ); injector.install( di::bind<int>::to(42) ); injector.create<app>();
[snip]
Thanks:) Anyway IMHO runtime binding may have some abilities of static checking, but exceptions will have to be thrown there either way. Separately compiling bindings definitely might be achieved with static checking for example with usage of forward declarations or composition root approach.
It doesn't need to do the fancy stuff, though it is (almost?) all
possible in my experience. Somehow, Guice survives by detecting things
at runtime so I would take from their experience that it is not a
completely terrible proposition. Is it possible to automatically convert
a static injector to a dynamic one? This might be the best of both
worlds. That is, something like this:
di::injector<>
SomeModule() {
return di::make_injector(
di::bind

Somehow, Guice survives by detecting things at runtime so I would take from their experience that it is not a completely terrible proposition.
Yea, but they didn't really have the opportunity to do it as well, did they? In the end C++ compile time checking possibilities are more complex than Java ones. I'm not saying runtime checking it's terrible proposition, but IMHO compile time checking will always be a better option, just because errors might be spotted earlier.
Is it possible to automatically convert a static injector to a dynamic one? This might be the best of both worlds. That is, something like this:
di::injector<> SomeModule() { return di::make_injector( di::bind
(), di::bind () ); } I think you have all the information you need to do this. I'd encourage you to continue to develop the "dynamic" portion in a way that you're happy with it. Personally, I'd be happy if the above worked.
Yea, its possible to convert static injector to dynamic one.
Presented proof-of-concept
(https://github.com/krzysztof-jusiak/di_runtime_injector)
was able to do so. There was one thing which couldn't be implemented
in runtime tho - which was interface deduction.
di::injector<> injector = di::make_injector(
di::deduce<implementation>()
// didn't work properly in runtime, use di::bind

On 2014-07-29, 1:04 PM, Kris wrote:
Somehow, Guice survives by detecting things at runtime so I would take from their experience that it is not a completely terrible proposition.
Yea, but they didn't really have the opportunity to do it as well, did they? In the end C++ compile time checking possibilities are more complex than Java ones. I'm not saying runtime checking it's terrible proposition, but IMHO compile time checking will always be a better option, just because errors might be spotted earlier.
Is it possible to automatically convert a static injector to a dynamic one? This might be the best of both worlds. That is, something like this:
di::injector<> SomeModule() { return di::make_injector( di::bind
(), di::bind () ); } I think you have all the information you need to do this. I'd encourage you to continue to develop the "dynamic" portion in a way that you're happy with it. Personally, I'd be happy if the above worked.
Yea, its possible to convert static injector to dynamic one. Presented proof-of-concept (https://github.com/krzysztof-jusiak/di_runtime_injector) was able to do so. There was one thing which couldn't be implemented in runtime tho - which was interface deduction.
di::injector<> injector = di::make_injector( di::deduce<implementation>() // didn't work properly in runtime, use di::bind
() instead ); But it's not as big deal I guess, although make both interfaces a bit different :/ Anyway I can implement runtime injector fairly easy. I'm just still not totally convinced it's really worth to do so, but definitely runtime injector might be an option to choose from in the end?
I'm ONLY interested in it for one reason: separately compiling bindings.
I don't care about anything else. You are 100% correct that I prefer
compile-time to run-time. One observation: each application will usually
have a root "app" type, but separate modules will not. So it doesn't
make sense to return an injector with a root type there.
I promise you that I will try and use your library soon when I find a
round tuit and perhaps at that point, I will find your solution is
sufficient, but in the meantime I can only suggest things based on my
personal experience.
So, the kind of code I'd like to write. This is probably nonsense for
your library, but I'm hoping it makes some sort of sense:
// ModuleA.cpp
#include "boost/di/whatever.hpp"
class this; class that; class animal; class frog; // ??
????
ConfigureModuleA() {
return make_injector(
bind

I'm ONLY interested in it for one reason: separately compiling bindings. I don't care about anything else. You are 100% correct that I prefer compile-time to run-time. One observation: each application will usually have a root "app" type, but separate modules will not. So it doesn't make sense to return an injector with a root type there.
Great, really happy to heart that. Hmm, when composition root is applied then only one 'app' will be created, so it means that all modules should be merged into one configuration, preferably in the main function and 'app' should be created. int main() { auto injector = make_injector(module1(), module2(), module3(), ...); return injector.create<app>().run(); } But yea, it totally depends on how dependency injection is applied and I have seen situations when objects were created out of the modules as well, so I understand your case.
I promise you that I will try and use your library soon when I find a round tuit and perhaps at that point, I will find your solution is sufficient, but in the meantime I can only suggest things based on my personal experience.
Thank you :)
Is something like this already possible? The syntax is not important, but how would you separately compile these two modules?
I guess your example is possible to write in the current version of the
library, but I'm not sure
if I do understand it correctly. ModuleA has some configuration and might be
compiled separately, this seems to be clear, but I do not understand what is
the responsibility of ModuleB then? Why there is 'app' there? Seems like
ModuleB might be empty, because all requirements for creating 'app' might be
found in ModuleA, unless you would like app to be bound in a different
scope?
If scope in which 'app' should changed then di::deduce<app> will expand to
unique scope, because app was created on stack? Besides scope customization
there is no need to specify which classes might be constructed using the
module, because it just depends on configuration - if bindings let you
create your class then its fine.
// ModuleA.hpp
#include "boost/di.hpp"
class this;
class that;
class animal;
class frog;
class ModuleA {
using injector = di::injector<
di::bind

On 2014-07-31, 10:46 AM, Kris wrote:
I guess your example is possible to write in the current version of the library, but I'm not sure
I think you are correct, but can we make one more adjustment to the example and you tell me how it would be written? Say the types are as follows: struct this { // lets keep pretending 'this' is not a keyword virtual ~this(); virtual void whatever() = 0; }; struct app { app(this*); } struct thing1{}; struct thing2{}; struct that : public this { that(thing1, thing2); virtual void whatever(); }; Say, further, that the 'that' type is defined separately from 'this' and 'app'. How would you configure it so that in one module (separately compiled), you bind 'app' and in another module, you bind the rest? Thanks for your patience! Sohail

Hi Sohail,
As far as I understand the whole idea is to have one module which will be
able to create app on it owns and second
module which will have other bindings required. And, of course, both modules
should be separately compiled.
I implemented the example you have introduced according to given assumptions
and think I have managed to meet the requirements, but we shall see.
So, the whole idea is simple:
* module1
* in order to be able to create app binding for this/that is
required
* provides interface to create app which will be separately compiled
* module2
* provides binging to module1 which will use it to create app
* [optional] provides any other bindings
* main
* creates injector with module2 and creates app - knowledge of app
is not required, is hidden in modules cpp files
So, to make the example a bit more complex, let's say we have object world,
which require app and some other stuff.
struct world {
world(std::unique_ptr<app>, std::shared_ptr
participants (3)
-
Kris
-
Krzysztof Jusiak
-
Sohail Somani