Thank you,
Corina & Emil & Families
Zhenya & Nadja
Vitaliy & Vika
Sveta & Andi
Oscar & Vicky
Mariana & Daniel
Natalia & Gonzalo
Rosalia & Silvio
Olya
Yuliya
Andrey
Colleagues
Given the static nature of the C++ type system, the lack of reflection and a platform-independent ABI in native C++, dependency injection doesn’t seem to have fallen into fashion in C++ projects [1. Alternative areas: COM & .NET ]. It might be that using the right tool for the right job diminishes the need for dependency injection containers for C++. Nonetheless, I find it worth digging into today’s offer of different libraries. Along the way, several other technologies will shine.
The resulting code will be constantly updated at https://github.com/d-led/test-ioc-cpp.
The project is expected to compile and run on many desktop platforms with a c++11 compiler. Hence, the makefiles are made with premake and a small helper library.
Mocks are useful when you use dependency inversion, hence the googlemock library will be used for demonstration purposes. Currently, the whole project is a single googletest executable, which is run after each compile.
Some makefiles and a Visual Studio solution are checked in, but can be generated from the premake script anytime.
The plan is to introduce each DI library superficially at first, and then to dig for feature differences, common patterns and perhaps applicability suggestions. The quest over the available libraries might take some time. The first intent is to give hello world examples of C++ DI libraries in action.
Linux builds and test runs will be performed automatically at https://travis-ci.org/d-led/test-ioc-cpp.
The Single Responsibility Principle is ingrained in all of the Dependency Injection libraries, as the instances are managed via smart pointers, which take up the responsibility of memory management.
An artificial, simple, decoupled architecture is used in the example. The interfaces are as follows:
struct IRender { virtual std::string Render() = 0; virtual ~IRender() {} }; struct IGetKeyValue { virtual size_t Count() = 0; virtual std::string GetKey(size_t pos) = 0; virtual std::string GetValue(std::string const& key) = 0; virtual ~IGetKeyValue() {} }; struct IModel { virtual std::string Get() = 0; virtual ~IModel() {} };
The names might change during the quest. The expected dependencies are as follows: the renderer needs a model, which is a key-value pair collection, which might be decoded from some kind of data_source, which is here simply a string.
To start with the quest, let’s look at Wallaroo. This has been the first library that I’ve considered using, and have already written a little walkthrough: lul.
The library has a rather exotic DSL for a DI library, however, it seems to be very well suited for communicating the concepts.
The objects (components) are created within a so-called catalog and have string IDs. When all the necessary objects have been created, they can be wired together with a DSL in the form:
within the catalog, use component_1 as dependency_1 of component_2.
Let’s see how the components that are usable within Wallaroo are defined. A drawback of Wallaroo can be seen from the start, namely that the components have to be defined intrusively – they have to derive from wallaroo::Part
:
class WallarooKeyRenderer : public wallaroo::Part , public IRender { ... };
The drawback can be somewhat overcome by using the Bridge pattern with composition when refactored. The first implementation here is fully dependent on wallaroo::Part
.
The library allows static registration, hence there is no need to expose the type or even a manually written factory of WallarooKeyRenderer
:
WALLAROO_REGISTER( WallarooKeyRenderer );
is the registration macro.
Dependencies are called wallaroo::Collaborator
s. These can be private members:
wallaroo::Collaborator< IGetKeyValue > model;
and must be initialised in the constructor:
WallarooKeyRenderer(): model( "model", RegistrationToken() ) { }
Then, if wiring the objects succeeds, the collaborators are usable as smart-pointers:
... model->GetKey(pos);
A nice feature in Wallaroo is that it supports constructor parameters. Another one – an initial support for loading plugins from DLLs exists. The configuration of catalogs: instances of objects and their wirings can be loaded from XML or JSON.
Full source: wallaroo_render.cpp
There are two implementations of the interfaces: a JSON decoder (using the lightweight single-header picojson library) and a renderer described above. The model can be simply mocked using GMock.
Inside the test (test_wallaroo.cpp) the model can be instantiated as follows.
Given the mock implementation:
class MockModel : public IModel, public wallaroo::Part { public: MOCK_CONST_METHOD0(Get, std::string()); }; WALLAROO_REGISTER( MockModel );
one can now instantiate the mock inside the test and set expectations using the GMock DSL:
catalog.Create("mock model","MockModel"); std::shared_ptr<MockModel> mock_model = catalog["mock model"]; ASSERT_TRUE( mock_model.get() ); EXPECT_CALL(*mock_model, Get()) .Times(AtLeast(1)) .WillRepeatedly(Return("{ \"a\" : 1 , \"b\" : 2 }"));
The other objects are instantiated in the fixture:
catalog.Create("renderer", "WallarooKeyRenderer");
and
CreateDecoder("decoder",catalog);
using an example of a factory with inverted control, in case one doesn’t want to let out strings that represent types, but cannot be checked at runtime:
void CreateDecoder(std::string const& name,wallaroo::Catalog& catalog) { catalog.Create(name, WallarooJsonDecoder); }
That way you neither leak the implementation type, neither its typename string.
Now that the objects are instantiated, they can be wired in code:
wallaroo_within(catalog) { use("decoder").as("model").of("renderer"); use("mock model").as("data_source").of("decoder"); }
A feature I enjoy in Wallaroo is that you can rewire the dependencies at runtime without creating new objects. That might get you some weird runtime dependencies if your code becomes messy, but if you take care of the quality, that feature might get handy.
When everything is wired up, the test is concluded with
ASSERT_EQ( "a,b", renderer->Render() );
Wallaroo is an easy to use header-only library, resembling in its use the Multiton Pattern. If instances of the dependencies can be given unique ids, there may be multiple implementations of the same interface, and if the dependencies should be rewired at runtime, wallaroo is a perfect choice.
05.11.2014: new wallaroo metaphors, +di, +infectorpp
More to come…