views:

3128

answers:

8

This is also a question that I asked in a comment in one of Miško Hevery's google talks that was dealing with dependency injection but it got buried in the comments.

I wonder how can the factory / builder step of wiring the dependencies together can work in C++.

I.e. we have a class A that depends on B. The builder will allocate B in the heap, pass a pointer to B in A's constructor while also allocating in the heap and return a pointer to A.

Who cleans up afterwards? Is it good to let the builder clean up after it's done? It seems to be the correct method since in the talk it says that the builder should setup objects that are expected to have the same lifetime or at least the dependencies have longer lifetime (I also have a question on that). What I mean in code:

class builder {
public:
    builder() :
        m_ClassA(NULL),m_ClassB(NULL) {
    }
    ~builder() {
        if (m_ClassB) {
            delete m_ClassB;
        }
        if (m_ClassA) {
            delete m_ClassA;
        }
    }
    ClassA *build() {
        m_ClassB = new class B;
        m_ClassA = new class A(m_ClassB);
        return m_ClassA;
    }
};

Now if there is a dependency that is expected to last longer than the lifetime of the object we are injecting it into (say ClassC is that dependency) I understand that we should change the build method to something like:

ClassA *builder::build(ClassC *classC) {
    m_ClassB = new class B;
    m_ClassA = new class A(m_ClassB, classC);
    return m_ClassA;
}

What is your preferred approach?

+2  A: 
MadKeithV
+7  A: 

This talk is about Java and dependency injection.

In C++ we try NOT to pass RAW pointers around. This is because a RAW pointer have no ownership semantics associated with it. If you have no ownership then we don't know who is responsible for cleaning up the object.

I find that most of the time dependency injection is done via references in C++.
In the rare cases where you must use pointers, wrap them in std::auto_ptr<> or boost::shared_ptr<> depending on how you want to manage ownership.

I would also point out that C++ and Java styles of programming are now so divergent that applying the style of one language to the other will inevitably lead to disaster.

Martin York
c++ leads to disaster. java **is** a disaster.
Matt Joiner
Martin, does this mean that you inject all dependencies as references, and you copy them inside the dependent object ?
phtrivier
@phtriver: No. It all depends on the situation. But usually we pass a reference and then inside the object we maintain that reference.
Martin York
+2  A: 

In C++, normally, when you done things right, you don't need to write destructors at all in most cases. You should use smart pointers to delete things automatically. I think, builder don't looks like the owner of the ClassA and ClassB instances. If you don't like to use smart pointers, you should think about objects life time and their owners.

Lazin
+3  A: 

Use RAII.

Handing a raw pointer to someone is the same as handing them ownership. If that's not what you want to do, you should give them some kind of facade that also knows how to clean up the object in question.

shared_ptr<> can do this; the second argument of its constructor can be a function object that knows how to delete the object.

Kaz Dragon
+4  A: 

This is interesting, DI in C++ using templates:

http://adam.younglogic.com/?p=146

I think the author is making the right moves as to not translate Java DI into C++ too literally. Worth the read.

Igor Zevaka
Very nice. Thanks for the suggestion.
Yorgos Pagles
+1  A: 

Based on my own experience, it is best to have clear ownership rules. For small concrete objects, it is best to use direct copy to avoid cross dependency.

Sometimes cross dependency is unavoidable, and there is no clear ownership. For example, (m) A instances own (n) B instances, and certain B instances can be owned by multiple As. In this case, the best approach is to apply reference counting to B, in the way similar to COM reference counting. Any functions that take possession of B* must increase reference count first, and decrease it when releasing the possession.

I also avoid using boost::shared_ptr as it creates a new type (shared_ptr and B* become two distinct types). I found that it brings more headaches when I add methods.

Sherwood Hu
Usually, when I encounter situations like your A and B example. The ownership of both A and B belongs to a higher level class, usually the creator of the A classes. In my experience there is almost always a single owner, and that is the way to avoid shared_ptr. Reimplementing a shared_ptr for yourself (ref counting) sounds like a breach of the DRY principle (do not repeat yourself), and as with all repetitions is increases the prob. of a bug occuring.
daramarak
A: 

I've recently been bitten by the DI bug. I think it solves a lot of complexity problems, especially the automated part. I've written a prototype which lets you use DI in a pretty C++ way, or at least I think so. You can take a look at the code example here: http://codepad.org/GpOujZ79

The things that are obviously missing: no scoping, no binding of interface to implementation. The latter is pretty easy to solve, the former, I've no idea.

I'd be grateful if anyone here has an opinion on the code.

cheez
I've posted the code on bitbucket now: http://bitbucket.org/cheez/dicpp - I still have to do provider injection but I think that is easily done. Scoping is also implemented.
cheez
PS: Provider injection done.
cheez
A: 

You can also check the FFEAD Dependency Injection. It provides DI on the lines of Spring for JAVA and has a non-obtrusive way of dealing with things. It also has a lot of other important features like in-built AJAX Support,Reflection,Serialization,C++ Interpreter,Business Components For C++,ORM,Messaging,Web-Services,Thread-Pools and an Application Server that supports all these features. The source code is not yet there, but i will be uploading it in some time, still working on the WINDOWS implementation, the LINUX implementation is done.

Sumeet