Is there any interest in a dependency injection library?

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
12 messages Options
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Is there any interest in a dependency injection library?

Kris
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

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Is there any interest in a dependency injection library?

Sohail Somani-2
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
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Is there any interest in a dependency injection library?

Kris
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 <[hidden email]>
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
>

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Is there any interest in a dependency injection library?

Sohail Somani-2
In reply to this post by Sohail Somani-2
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



_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Is there any interest in a dependency injection library?

Kris
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_examples.html#di.examples.more_examples.modules_hpp_cpp

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
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Is there any interest in a dependency injection library?

Sohail Somani-2
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

_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Is there any interest in a dependency injection library?

Kris
> 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
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Is there any interest in a dependency injection library?

Sohail Somani-2
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


_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Is there any interest in a dependency injection library?

Kris
> 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
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Is there any interest in a dependency injection library?

Sohail Somani-2
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


_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Is there any interest in a dependency injection library?

Kris
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
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Is there any interest in a dependency injection library?

Sohail Somani-2
On 2014-08-04, 8:12 AM, Kris wrote:
>
> Hope example will be helpful.
>
> Thanks for your feedback, Kris

Very helpful, thanks. I'm not 100% convinced that I can use it in place
of my own, but I'm going to try at some point.

Sohail


_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Loading...