Contents
Introduction
In Part 1 I have started with a list of C++ dependency injection libraries, and sketched an example problem, solved and tested with the help of Wallaroo. For the second part, Hypodermic should have been in focus, but unfortunately, no version compiled out of the box on MacOS with Clang [1. To do in 2014]. Hence, the article is about sauce, an inspiration from google-guice.
Dependency Injection using sauce
A little repetition, the example problem contains of 3 interfaces with a linear dependency graph of the implementations. Not to repeat the implementations, and not change the structure of the existing code, simple factories for the default implementations have been exposed, but not the concrete types in the headers, i.e. for the default renderer implementation:
std::shared_ptr< IRender > NewKeyRenderer(std::shared_ptr< IGetKeyValue > model) { return std::make_shared<KeyRenderer>(model); };
Implementations
Simplified, the sauce solution looks as follows. Unlike in Wallaroo, in sauce, the implementations do not need to derive from a common “dependency” class. Any type can be bound to any derived class. The implementation of a renderer for the sauce example just adds a delegation to the original implementation [2. Efficiency and overhead are not considered in the first place.]:
class SauceKeyRenderer : public IRender { public: SauceKeyRenderer(std::shared_ptr< IGetKeyValue > m) : pimpl ( NewKeyRenderer(m) ) { } public: virtual std::string Render() { //... return pimpl->Render(); } private: std::shared_ptr< IRender > pimpl; };
Modules
Modules are containers of bindings of interfaces to their implementations. The easiest way to define a module is to provide a function with the signature void (* module)(sauce::Binder &)
.
Here’s the self-explaining module:
void render_module(sauce::Binder& b) { b.bind<IRender>().to<SauceKeyRenderer(IGetKeyValue&)>(); }
Once again, obviously, the type doesn’t have to leak outside the module due to inversion of control.
Note that the IGetKeyValue
dependency of SauceKeyRenderer
is injected via the constructor parameter, enabling automatic dependency resolution at later time.
Injectors
Instances of the bound interfaces are obtainable from injectors, which can be constructed from a collection of modules:
sauce::shared_ptr<Injector> injector = Modules() .add(render_module) .add(decoder_module) .add(model_module) .createInjector() ;
Resolving the dependencies
The hello-world example of resolving the dependencies is one simple line:
sauce::shared_ptr<IRender> renderer = injector->get<IRender>();
In this case, SauceKeyRenderer
will get resolved and two other dependencies will be automatically instantiated and injected (see test_sauce.cpp).
Mocking and singletons
Sauce allows for different lifetime models of the interface instances. Singletons are possible with the concept of scoped injectors, in which, the instances are shared.
For googlemock getting the resolved instance is crucial, since the expectations are bound to an instance of the mocked class. With automatic dependency resolution and no hacks, the interface IModel
would resolve to a new instance in the test. Hence, unless a way to use the shared instance lifetime inside the test is found, the mock is implemented trivially without googlemock.
Summary
This article is once again a short intro into the paradigm of another Dependency Injection library sauce. A more elaborate intro can be read in sauce’s tutorial test suite.
Source: https://github.com/d-led/test-ioc-cpp
More to come…
Pingback: Dependency Injection Libraries in C++. Part 1: Wallaroo, Introduction
Pingback: Dependency Injection Libraries in C++. Part 1: dicpp, Introduction
Great article addressing dependency injection in C++. I implemented yet another C++ dependency
injection framework, which recently was proposed for boost – https://github.com/krzysztof-jusiak/di –
library is macro less (free), header only, C++03/C++11/C++14 library providing type safe, compile time, macro free constructor dependency injection.
Hi Kris! I’m glad you liked it, and thanks for the comment! I’d be glad to try your library out, as I’ve seen it on github, but haven’t got around doing so yet. If you find time, feel free to incorporate it into the test project and make a pull request.
you found so many other libraries :). I guess, I might give up on the quest of trying them all out