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 <boost/shared_ptr.hpp> *#include <boost/di.hpp>* *namespace di = boost::di;* struct interface { virtual ~interface() { } }; struct implementation : interface { }; class hello_world { public: hello_world(std::unique_ptr<interface> up , const std::shared_ptr<interface>& sp1 , boost::shared_ptr<interface> sp2 , int i) : i(i) { assert(dynamic_cast<implementation*>(up.get())); assert(dynamic_cast<implementation*>(sp1.get())); assert(dynamic_cast<implementation*>(sp2.get())); assert(sp1.get() == sp2.get()); assert(i == 0); } int run() { return i; } private: int i = 0; }; int main() { // create injector and configure bindings auto injector = *di::make_injector*( *di::bind*<interface, implementation>() // scope will be deduced ); // create and run hello_world return injector.*create*<hello_world>().run(); } <---------------------------------------------------------------------------------------------------------- *Overview*: * boost library incubator: http://rrsd.com/blincubator.com/bi_library/di-dependency-injection-2 * repository: https://github.com/krzysztof-jusiak/di * documentation: http://krzysztof-jusiak.github.io/di/boost/libs/di/doc/html * issues: https://github.com/krzysztof-jusiak/di/issues * download: https://github.com/krzysztof-jusiak/di/archive/master.zip *Supported Compilers* - 100% test coverage: * Linux (x86/x86-64) * Clang 3.2/3.3/3.4/3.4.2+ (clean with Clang Static Analyzer and Valgrind) * GCC 4.7.3/4.8.2/4.9.0+ (clean with Valgrind) * Intel C++ 14.0.3+ (clean with Valgrind) * Windows (x86/x86-64) * MinGW 4.7.3/4.8.2+ * Visual Studio 2013+ (clean with DrMemory) * Darwin/Mac OS (x86-64) * Clang 503.0.40+ (Apple LLVM version 5.1) *Continuous integration*: * Linux, Darwin/Mac OS: https://travis-ci.org/krzysztof-jusiak/di * Windows: https://ci.appveyor.com/project/krzysztof-jusiak/di * Coverage: https://coveralls.io/r/krzysztof-jusiak/di *Similar libraries*: * https://bitbucket.org/cheez/dicpp * https://code.google.com/p/infectorpp * https://github.com/phs/sauce * https://code.google.com/p/wallaroo * https://code.google.com/p/hypodermic * https://github.com/admiyo/CppInject * http://qtioccontainer.sourceforge.net * https://code.google.com/p/autumnframework * http://sourceforge.net/projects/cpp-resolver (proposal for boost library in 2009: http://lists.boost.org/Archives/boost/2009/10/157477.php) * https://code.google.com/p/pococapsule * https://code.google.com/p/spring-cpp * http://www.cs.utah.edu/~arichard/inject.html * http://bobah.net/d4d/source-code/misc/cpp-dependency-injection * https://code.google.com/p/ffead-cpp * http://codebros.github.io/DiLite * https://code.google.com/p/kindi Java: https://github.com/google/guice C#: http://www.ninject.org/ Source code was internally reviewed. DI was successfully used in a commercial project as well. Looking forward to hear from you. Any feedback is more than welcome. Best regards, Kris
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<TInjector*>(injector_.get())->template create<T>(); }) { } T create() const { return f_(); } private: unique_ptr<void> injector_; function<T()> f_; }; Having dynamic_injector in place we won't need any information in module header file except forward declaration of app. In module cpp file we include all required dependencies, create configuration and return dynamic_injector. ###module.hpp #include "dynamic_injector.hpp" class app; // forward declaration class module { public: dynamic_injector<app> configure() const; }; ###module.cpp #include "module.hpp" #include "logic.hpp" // include in cpp is okay #include "logger.hpp" // include in cpp is okay namespace di = boost::di; dynamic_injector<app> module::configure() const { return di::make_injector( di::deduce<logic>() // or di::bind<ilogic, logic>() , di::deduce<logger>() // or di::bind<ilogger, logger>() ); } Right now we just have to create our module and create app in main - yea, we limited ourselves to create only app types, but actually it is a good thing in general, because then we are forced to apply composition root. Anyway nothing is stopping us from making dynamic_injector with type list instead as well. ###main.cpp #include "module.hpp" #include "app.hpp" int main() { auto injector = module().configure(); return injector.create<app>().run(); } Hope this example was helpful and answer your really valid question. *>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". Kris On Fri, Jul 25, 2014 at 1:58 PM, Sohail Somani <sohail@taggedtype.net> wrote:
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<TInjector*>(injector_.get())->template create<T>(); }) { } T create() const { return f_(); } private: unique_ptr<void> injector_; function<T()> f_; }; Having dynamic_injector in place we won't need any information in module header file except forward declaration of app. In module cpp file we include all required dependencies, create configuration and return dynamic_injector. ###module.hpp #include "dynamic_injector.hpp" class app; // forward declaration class module { public: dynamic_injector<app> configure() const; }; ###module.cpp #include "module.hpp" #include "logic.hpp" // include in cpp is okay #include "logger.hpp" // include in cpp is okay namespace di = boost::di; dynamic_injector<app> module::configure() const { return di::make_injector( di::deduce<logic>() // or di::bind<ilogic, logic>() , di::deduce<logger>() // or di::bind<ilogger, logger>() ); } Right now we just have to create our module and create app in main - yea, we limited ourselves to create only app types, but actually it is a good thing in general, because then we are forced to apply composition root. Anyway nothing is stopping us from making dynamic_injector with type list instead as well. ###main.cpp #include "module.hpp" #include "app.hpp" int main() { auto injector = module().configure(); return injector.create<app>().run(); } Hope this example was helpful and answer your really valid question. 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".
Kris On Fri, Jul 25, 2014 at 1:58 PM, Sohail Somani <sohail@taggedtype.net> wrote:
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<TInjector*>(injector_.get())->template create<T>(); }) { } T create() const { return f_(); } private: unique_ptr<void> injector_; function<T()> f_; }; Having dynamic_injector in place we won't need any information in module header file except forward declaration of app. In module cpp file we include all required dependencies, create configuration and return dynamic_injector. ###module.hpp #include "dynamic_injector.hpp" class app; // forward declaration class module { public: dynamic_injector<app> configure() const; }; ###module.cpp #include "module.hpp" #include "logic.hpp" // include in cpp is okay #include "logger.hpp" // include in cpp is okay namespace di = boost::di; dynamic_injector<app> module::configure() const { return di::make_injector( di::deduce<logic>() // or di::bind<ilogic, logic>() , di::deduce<logger>() // or di::bind<ilogger, logger>() ); } Right now we just have to create our module and create app in main - yea, we limited ourselves to create only app types, but actually it is a good thing in general, because then we are forced to apply composition root. Anyway nothing is stopping us from making dynamic_injector with type list instead as well. ###main.cpp #include "module.hpp" #include "app.hpp" int main() { auto injector = module().configure(); return injector.create<app>().run(); } Hope this example was helpful and answer your really valid question. 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".
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::injector<di::deduce<implementation>>; public: injector configure() const; }; #module.cpp #include "module.hpp" #include "implementation.hpp" module::injector module::configure() const { return di::injector<di::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<interface, implementation>() ); injector.install( di::bind<int>::to(42) ); injector.create<app>();
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<interface, implementation>() ); 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<ilogger,logger>(), di::bind<ilogic,logic>() ); } 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. Sohail
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<ilogger,logger>(), di::bind<ilogic,logic>() ); }
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<interface, implementation>() 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? 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-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<ilogger,logger>(), di::bind<ilogic,logic>() ); }
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<interface, implementation>() 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<this,that>(), bind<animal,frog>() ); } // app.hpp class app { explicit app(this*t); // requires this (bound to that) }; // ModuleB.cpp #include "boost/di/whatever.hpp" // I think you need to have these, but I'm not sure if you can do it without it #include "this.hpp" #include "app.hpp" ???? ConfigureModuleB() { return make_injector( deduce<app>() // not sure if I'm using deduce correctly ); } // main.cpp #include "boost/di/whatever.hpp" int main() { injector inj{ConfigureModuleA,ConfigureModuleB}; inj.construct<app>().run(); } Is something like this already possible? The syntax is not important, but how would you separately compile these two modules? Thanks! Sohail
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<this,that>, di::bind<animal,frog> >; public: injector configure() const; }; // ModuleA.cpp #include "ModuleA.hpp ModuleA::injector ModuleA::configure() const { return injector(); } // app.hpp class this; class app { explicit app(this*t); // requires this (bound to that) }; // ModuleB.hpp class ModuleB { using injector = di::injector<>; public: injector configure() const; }; // ModuleB.cpp #include "boost/di/whatever.hpp" ModuleB::injector configure() const { return injector(); } // main.cpp #include "boost/di.hpp" #include "ModuleA.hpp" #include "ModuleB.hpp" int main() { auto injector = di::make_injector(ModuleA(), ModuleB()); return injector.create<app>().run(); } Cheers, 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-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<other_stuff>); }; Right now to create world we only need to recompile module2. auto injector = di::make_injector(module2()); (void)injector.create<world>(); which is the true for all headers provided, since all the implementation is in cpp files. ///////////////////////////////////////-> start here // things.hpp #ifndef THINGS_ZIPI1EVE #define THINGS_ZIPI1EVE struct thing1 {}; struct thing2 {}; #endif // this.hpp #ifndef THIS_U2KTF9DP #define THIS_U2KTF9DP struct this_ { virtual ~this_() { } virtual void whatever() = 0; }; #endif // that.cpp #include "that.hpp" that::that(std::unique_ptr<thing1>, std::unique_ptr<thing2>) { } void that::whatever() { } // that.hpp #ifndef THAT_LPR4I07J #define THAT_LPR4I07J #include <memory> #include "this.hpp" #include "things.hpp" struct that : public this_ { that(std::unique_ptr<thing1>, std::unique_ptr<thing2>); void whatever() override; }; #endif // app.hpp #ifndef APP_P3WNIP9Z #define APP_P3WNIP9Z #include <memory> class this_; struct app { app(std::unique_ptr<this_>); }; #endif // app.cpp #include "app.hpp" #include "this.hpp" app::app(std::unique_ptr<this_>) { } #endif // module.hpp #ifndef MODULE_UNMB1JJ2 #define MODULE_UNMB1JJ2 #include <boost/di.hpp> namespace di = boost::di; class that; class module { using injector = di::injector< di::deduce<that> // or di::bind<this_, that> >; public: injector configure() const; template<typename T> T create(); }; #endif // module.cpp #include "module.hpp" #include "that.hpp" #include "app.hpp" module::injector module::configure() const { return injector(); // runtime values binding not required } template<> std::shared_ptr<app> module::create<std::shared_ptr<app>>() { return injector().create<std::shared_ptr<app>>(); } // module2.hpp #ifndef MODULE2_UNMB1JJ2 #define MODULE2_UNMB1JJ2 #include <boost/di.hpp> namespace di = boost::di; class app; class module2 { using injector = di::injector< decltype(di::bind<app>::to(std::make_shared<app>())) //other bindings required >; public: injector configure() const; }; // module2.cpp #include "module2.hpp" #include "module.hpp" #include "app.hpp" #include "things.hpp" #include "that.hpp" module2::injector module2::configure() const { return injector( di::bind<app>::to(module().create<std::shared_ptr<app>>()) ); } // main.cpp #include <boost/di.hpp> #include "module2.hpp" class app; namespace di = boost::di; int main() { auto injector = di::make_injector(module2()); (void)injector.create<std::shared_ptr<app>>(); return 0; } ///////////////////////////////////////-> ends here Hope example will be helpful. 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.
participants (3)
-
Kris
-
Krzysztof Jusiak
-
Sohail Somani