One goal of object oriented programming is to encapsulate functionality within a class. If one class requires another in order to collaborate on a larger process, the programmer must deal with the wiring up of the two classes. Much has been written about the correct way to do this in various object oriented languages. The term “Inversion of Control” and the related concept of “Dependency Injection” have become part of the common language of software development, at least in the Java world, due to projects like Pico/Nano container, The Spring framework, and earlier efforts inside J2EE and ATG Dynamo. These frameworks make use of the introspection mechanisms in Java to create instances of classes based on demand criteria.
Updated: links and using the code formatter. Changed Class from Single Letter to full names to be clearer and to not conflict with HTML tags.
One drawback of the introspection mechanism is that it works outside of the type system. A programmer can write code that compiles, but is semantically incorrect. The only way to test this code is by running it. Since C++ lacks the introspection mechanism of Java, this approach cannot be ported to C++. However, C++ Has a much richer system for developing type based programs anyway: templates. Many of the techniques from Alexandrescu’s Modern C++ Design can be linked together to build an inversion of control mechanism that is type safe. An added benefit is that the mechanism suffers from none of the introspection overhead of the Java approach, and thus can be used inside performance critical code.
The general approach is to register a construction and destruction function for a given class with a factory for that class. The factory is a class template that supports partial template specialization, allowing multiple definitions for a class. The factory functions are capable of calling other factory functions, to fetch and create dependencies for the class in question. For instance: Let’s define a class Stand which stands alone. Additionally, we define class Dep which depends on class Stand.
In C++ Objects can be allocated from three main memory areas: the data segment, the stack, and the heap. When the life span of an object must extend beyond the scope of the current stack frame, the object cannot be allocated on the stack. To allocate on the heap, the programmer writes something like:
Stand* stand = new Stand; Dep* dep = new Dep(stand); |
This is a method of object creation: it is not possible to use the ‘new’ operator to share an instance of ‘stand’ that already exists. Thus we are linking the creation and usage of the classes together. If the programmer wishes to use a globally scoped instance of the object, he can do so using the fully qualified name of the instance. More complicated approaches include creating an intermediate object that acts as a monitor for a pool of available objects.
The parameter ‘stand’ to cxonstructor Dep() above allows instances of Dep to work with instances of Stand regardless of the creation method. Writing the classes like ‘Stand’ and ‘Dep’ require something external to wire them up. This is intentional, as the method of creation of ‘stand’ is a policy decision that is outside the scope of ‘Dep’. If the code is written exactly like the example above, the policy is hard coded to be a ‘new’. Ideally, we would separate this policy from the usage of the classes.
I am now going to introduce a class template called ‘supply’. A supply is a class that maintains references to factories and instances produced by those factories. The policy of how to create and destroy these objects are registered with the Supplies when the application is initialized like this:
Supply< Dep > register(createDep,destroyDep); |
Where createDep and destroyDep are function pointers.
We can be succinct and omit the functions for create and destroy, in which case the supply is smart enough to just call the constructor and destructor. In another file we can register how to create and destroy L
Supply< Stand > register(); |
In order to fetch an instance of type Stand we call:
Stand * stand = Supply< Stand >.fetch(); |
This code can exist inside the createD function which would look like this:
Dep* createDep(){ Stand * stand = Supply< Stand >.fetch(); return new Dep(stand); } |
So far we have divided the construction logic from the usage by adding a single function to create the object graph on demand. The fetch function can be made smart enough to check if it has an instance available and, if so, to return that instance instead of calling the create function.
The level of Inversion of Control described thus far is fairly paltry, and would not be a valuable addition to most programs. It lacks at least two things to be truly useful. First, there needs to be a way to distinguish between two different instances of the same class. Even more important is the ability to scope those instances to a particular subdivision of the application. There are two techniques we can use to implement these features: either by calling a different function for each instance, or passing in a parameter that is used to distinguish between the instances. The key tool for selecting between two functions is partial template specialization. Specifically, we want to use a primitive value to distinguish between different creation policies. For instance, suppose an application requires several different database schemas. One pool is for on-line transaction processing (OLTP), one is for content management, and one is for integration with a legacy system. A class named DatabasePool controls connections to each database. We would need to register each DatabasePool separately:
Supply < DatabasePool, DBPOOL_OLTP > register(createOLTPPool, freeOLTPPool); Supply< DatabasePool, DBPOOL_CONTENT > register(createContentPool, freeContentPool); Supply< DatabasePool, DBPOOL_LEGACY > register(createLegacyPool, freeLegacyPool); |
Then when a class requires a DatabasePool to perform it’s work, it would register to resolve the dependency with:
Supply < DatabasePool, DBPOOL_LEGACY >.fetch() |
To scope an instance of a class to a specific portion of the application requires understand the application type. The types of applications written in C++ range from small to large. Here is an incomplete list:
- Embedded programming
- hand-held wireless device applications
- Desktop GUI applications
- System tray applets
- HTTP based applications
- streaming media network services
- TELNET style connection based network services
- Asynchronous messaging for systems integration
- Data intensive super computing (DISC)
- Massively parallel message passing interface (MPI) Supercomputing tasks.
The scopes for each of type of application is going to be somewhat different. In embedded programming for really small devices we can probably make most objects global. Something as complex as an dependency injection may be more than the device can handle. Working up the stack, we come across applications that require threading, and that may be handling multiple users at the same time. This adds a new requirement: our dependency injection mechanism must be thread safe. Here we can lean upon the lessons learned by many people that have been building enterprise applications in Java for many years. In the J2EE stack, in ATG Dynamo and now in the Spring framework, objects can be scoped globally, per application (assuming multiple apps run in the same container), per session, and per HTTP request. This is a pretty good breakdown of scopes, and applies to many application types beyond HTTP. For instance, a TELNET server would have one session per socket connection , and divide up the stream into smaller sequences that map to requests. An asynchronous messaging protocol would not have sessions, as state is contained completely inside the messages, but certain patterns like scatter/gather might have scopes that span multiple messages. Applications at the smaller and larger ends of the spectrum have a similarity in that the tasks tend to be the only thing running on the machine, and thus there is less of a need to distinguish between session, application and global scoping.
I’m going to describe the HTTP model with the hope that applying it to other types of applications will be done fairly similarly. The scopes from shortest to longest are : request, session, application, and global. When the network server starts running, the first global objects get initiated. These in turn activate the applications. One of these objects will listen for network connections and accept incoming requests. The first such request will force the creation of a session. Once the request is handled and the response is sent, the request object is freed along with all of its corresponding objects, while the session and associated objects remain in the application, usually on a timer that will free them after some period of no activity. The session is uniquely identified with a cookie sent with the response, and sent back to the server with each subsequent request. The Request object will then associate itself with that session. Once the hierarchy is established from request up to session, all object resolution requests are fulfilled via a chain of responsibility. The application specific code should only resolve via Supply<T>.fetch(Request). If the object requested is not registered at the request level, the call is forwarded on to Supply<T>.fetch(Session), and then on to Supply<T>.fetch(Application), and finally to Supply<T>.fetch() which resolves globally. Failure to resolve globally will trigger an exception.
The onus is upon the application developer to correctly register the scope of requested objects. Each object will then be created upon first demand, and reused until the lifetime had been completed. For example a DHCP server that receives a packet containing a ClientMACAddress will parse the field from the packet when first accessed, and then subsequently will use the parsed object until the DHCP request has been sent, at which time the ClientMACAddress will be freed.
Here is a sample implementation.
https://github.com/admiyo/CppInject
I had originally meant to expand the scope of this discussion to talk about entities stored inside a database, but quickly realized that it was at least as complex as the discussion above. If the entire database can be held in memory at once, you can make them global objects. Anything more than that requires a complicated caching scheme. That will be a separate article
Nice job, a reasonable proposal for depencency injection. The nod to Andrei A. was appropriate and timely. I think the use of this DI model expressed in some test code could be a good argument/discussion or blog entry.
I’ve posted the source code as an open source project here:
http://sourceforge.net/projects/cpp-resolver/
In C++ code we would traditionally write something like:
S* s = new S();
D* d = new D(s);
Make the first line of code
S * s = new S;
and you’re in business.
Also note that it isn’t obvious that the following isn’t appropriate:
S s;
D d(s);
You haven’t established any need for the objects to be on the free store.
Rob,
Thanks for the comments.
To address the second part: The objects don’t need to be on the freestore, since they are going to be freed on the exit from a specific stack frame. However, you would need to pre-allocate the space for all objects, even those that are not potentially used in a given code path, which is prohibitive.
The idea I went with is that the objects are going to be lazy allocated. I can think of other allocation schemes that would work in other manners. For instance, if you wanted to allocate all objects statically, you could do so. I’ll see if I can code up an example that does that.
There is much code out there classes like ‘D’ actually have the knowledge of how to create or fetch ’s’.
I cannot parse that sentence.
Instead of the phrase, templatized class, you should use the correct phrase: class template.
s/desty/destroy/
s/and instance/an instance/
s/an class/a class/
s/The scopes for each of/The scope for each/
s/an dependency/a dependency/
s/yeakmrs/years/
s/messagin/messaging/
s/THe scopes/The scopes/
s/recieves/receives/
s/at leas as/at least as/
And, since you’re consistent, s/http/HTTP/.
@Adam,
My point about the free store was just that you didn’t give any reason for the objects to be on the free store yet you said that doing so was “traditional.”
BTW, you should include some links in the opening to lead the reader to background information on DIP, etc.
Rob,
Thanks for your feedback. I greatly appreciate the time you took to proofread this post, and the quality of your criticism. I’ve updated the article. Thanks for dealing with my less-than-stellar spell checking: something I should have done better prior to asking for input.
Irony: s/To allocate on the head/To allocate on the heap/
BTW, I was just thinking you should say something like, “Stripping away the factory functions, creating a D instance amounts to code like the following,” rather than your data segment/stack/free store approach.
(You may delete my editorial comments and just leave their effect in your post.)
Here’s mine: http://bitbucket.org/cheez/dicpp/wiki/Home
It has an improvement over yours which is that it does not require the user to write their own factory functions.
Sohail,
I see we are on the same track. Maybe we are on to something here.
The user does not need to write factories for mine, it just is an option.
Example:
supply::configure();
Will register A Class with the default scope, factory and destroyer. The Default factory is a no-args constructor.
One thing I notice is the DI_Constructor in your code. One of my goals was to avoid modifying the objects managed, and thus to be totally non-obtrusive. I haven’t dug through your code enough to see if that is semantic sugar or a requirement.
One thing that became while developing this is that the use wants to separate the usage of the object from the lifespan. As such, you want to provide the registration of the factory for your object in a different part of the code from the implementation. If there is a custom factory, it belongs more with the registration code than the implementation.
Nice work.
Hey,
I did not realize that! Well, if I had, maybe I would have just stuck with yours. Probably not 🙂
You are correct, DI_CONSTRUCTOR is just sugar.
If I understand you correctly, the scope is particular to the application which is something that my library does allow for by accident.
Thanks for your comments, appreciate it!
Also, does your library do recursive object construction automatically? It wasn’t clear from the tests but mine does.
Sohail, Yes indeed, it creates objects on demand, so recursive works. I think we are pretty much on the same sheet of music as far as functionality.
One of my goals was to get the whole thing working with a single header file, and only header file includes. This is in keeping with the general strategy of stl and of boost.
It would have been nice if you did use mine, as it is starting to get lonely: I haven’t been doing much C++ lately. Ideally, we’d have a single framework as part of the C++ standard library.
Oh, I see you have moved your project to sourceforge! That would explain my confusion with why I thought the functionality was lacking. Going to read through the sf project now.
By the way, I don’t get an email when you reply. Don’t know if that’s your fault or mine.
Ah, I think you and I defer on definitions of automatic when recursing. For mine, the objects you need are passed to you in the constructor. Still, yours is good too because it abstracts where you fetch things from.
As for sticking to one implementation, sure, please feel free to use mine 😉
The sourceforge project is the older one. I’m hosting it now on github, as I prefer git to SVN. Course, I also don’t care about the Window’s platform, so not using a RCS that works on Windows isn’t a big deal.
http://github.com/admiyo/CppInject
I, too, only do dependency injection in the constructor. Look at the simple example, and it should be a lot clearer.
I’d love to see what a complex project built with either of our systems would look like. It would be great to take something non-trivial, like a web server, and see it implemented in both for aside by side comparison.
I think a web server would be really bad. Maybe you can take the stock quote application I wrote and translate it into yours? It’s very simple…
Here it is btw: http://bitbucket.org/cheez/dicpp/src/tip/lib/example/
Pingback: TDD Q&A « require ‘brain’
Valuable info and outstanding style you got here! I would like to finest wishes for sharing your thoughts and time into the stuff you post!
Take a look at my library:
http://code.google.com/p/infectorpp/
it emphasize RAII pattern, when you declare a depedency in the constructor you are already declaring its life-time wich is very good and relief the user from doing that with additional code. This is done in a total safe way (circular dependency is detected correctly, and in case of exceptions there are no leaks: even better you can still continue to use the Container because it remains unchanged!). Performance was not a goal but I found the compiler already greatly optimized most of stuff (the unit test file is in the order of only 90 KB!), in future releases I planned many improvements also to performance, but safety and ease of use are my main goals and with the actual 1.0.1 release I think I achieved them.
Nice work, Dario.
A couple comments:
“when you declare a depedency in the constructor you are already declaring its life-time ” I explicitly seek to decouple this. I want objects to be reusable, and reused in many different contexts. Imagine if all Strings had to be requests scope?
You want to have a context from which one object can find another, but I am thinking more and more that the context should be accessed via thread local storage.
Hungarian notation is a blight. The ‘I’ on the interfaces needs to go away.
Still, it looks like a decent library. If I find myself coding in C++ again, I’ll take a deeper look.
Thanks! I hope I can improve with more features soon. For lifetime management I think actually is enough what I did. When I’ll add nested containers to Infector++ then there will be even more control. But I already see deep differences, so I’m happy we are focusing on different goals (by the way I’m allergic to Hungarian notation but used it for documentation purposes because I was wrongly convinced that people easily understand what a big “I” means.. so yeah it is more like a questionable shortcut I took… lol ).
s/cxonstructor/constructor/
Pingback: Dependency Injection | Matthew's Life
Thanks for sharing.
I’m confused about the supply::release(Zone* zone).
@file bindings.cpp
register_all_bindings(){
supply::configure();
supply::configure(ConcreteZoneType::instance);//I have add a function in the supply class
}
@file resolve_test.cpp
main(){
Zone globalZone(&GlobalZoneType::instance);
Zone session1( &ConcreteZoneType::instance, &globalZone );
Zone request1( &ConcreteZoneType::instance, &session1 );
Zone request2( &ConcreteZoneType::instance, &session1 );
A *a = supply::fetch(request1);
a = supply::fetch(globalZone);
a = supply::fetch(request2);
supply::release(&request1);//I want to release the object at the request1-zone. In fact It will release the object at the session1-zone.
supply::release(&request2);//This will release the global object at the global-zone!!! so the supply::release() is not safe
}
Q:How to release an object manually? or the supply::release() should not be used explicitly.
supply <A>::configure();
supply <A>::configure(ConcreteZoneType <Session>::instance);
sorry, This is my first time to comment online.
The goal with my approach is that Objects are allocated on the Heap but Freed based on stack scope. The goal is that you allocate the objects as needed, all within some stack frame, and when the Zone at the root of the stack frame is popped off the stack and deleted, all of the objects that it manages are also cleaned up.
Do not explicitly release objects.
For a web server based application, the session will likely not really be on a stack scope, but more likely in a global object held by the web server and will be timed out. The request, on the other hand, will likely be in a stack frame, kicked off by whatever is at the top of the stack responding to that particular request. The assumption is that the request object will hold a parent Zone reference to the session object to keep it alive. But the requests will be shorter lived than the session, so objects allocated at request scope will be cleaned up, not objects at session scope. Those will only be freed when the Session is deleted.
Hi,
Isn’t that all only “advanced” version of singleton? Why should I inject anything when supply::fetch() give me any object everywhere I include supply?
Great question. It is not an advanced version of the singleton for a couple reasons. First, lets look at what defines a singleton:
The Wikipedia article states “the singleton pattern is a software design pattern that restricts the instantiation of a class to one object.” And with the DI approach here you could certainly have multiple instances of an object, they would just have to be separately resolvable. For example, if supply::fetch() uses a thread local context, each thread would get a different instance. Or, if you use some of the other resolution tools I built into the coder like you can see here https://github.com/admiyo/CppInject/blob/master/include/supply.h#L24 you can specify “which” as a way to distinguish between two instances of the same type…say different Database connections: one for website content, one for logging, one for user identity. Since which is defined as an int, content=0 logging=1 user=2. The impl there is incomplete (I’ve not used C++ professionally in a decade) it does not show that implementation, but it should be https://github.com/admiyo/CppInject/blob/master/include/supply.h#L88
static T* fetch(Zone& zone, int which)
and then static T* fetch(Zone& zone, int which) should resolve to
return fetch(zone, 0)
Reminds me of https://github.com/google/fruit
Pingback: Dependency Injection framework for C++ [closed] - ExceptionsHub
I do not know if it’s just me or if perhaps everyone else encountering issues with your
website. It appears like some of the text
on your content are running off the screen. Can someone else please provide feedback and
let me know if this is happening to them as well?
This may be a issue with my internet browser because I’ve had this happen previously.
Kudos
I used a different code formatter back then. It seems to be flakey. I’ve modernized other posts…guess it is time to do so for this one.
Pingback: Dependency Injection framework for C++ [closed] – Row Coding