Contents
Dependency Injection + C++ = dicpp
The first article introduced the example problem – an almost trivial example for dependency injection. One of the implementation classes is mocked within a test to show the resulting implementation configuration flexibility.
Another library in the quest is dicpp. It is somewhat similar to sauce with little twists, hence it required some trial and error to get right after sauce. Dicpp configures the dependencies in code and allows configurable lifetime scopes.
Modules
Modules are, again, similar to sauce’s modules, as they can be simple functions for registering the bindings of interfaces to implementations.
The library uses a slightly intrusive macro that puts a special typedef
in the class’ declaration. One constructor is supported and should be split into declaration and definition. The macro for the renderer looks as follows:
class DicppKeyRenderer : public IRender { public: DI_CONSTRUCTOR(DicppKeyRenderer,(std::shared_ptr< IGetKeyValue > m)); ... };
Without changing the existing factory, the default implementations are used:
DicppKeyRenderer::DicppKeyRenderer(std::shared_ptr< IGetKeyValue > m) : pimpl ( NewKeyRenderer(m) ) { }
The module with the bindings looks as follows (dicpp_module.cpp):
void dicpp_module( di::registry& r ) { r.add( r.type< IGetKeyValue >().implementation<DicppJsonDecoder>() ); r.add( r.type< IRender > ().implementation<DicppKeyRenderer>() ); }
Mock and the Singleton scope
To set the mock expectations on an instance of a dependency, one needs to obtain a pointer to that dependency. Once again, the mock implementation of an interface is done via googlemock:
class MockModel : public IModel { public: MOCK_METHOD0(Get, std::string()); };
To obtain the same instance of the mocked IModel
as which gets resolved automatically, one can use a dedicated scope, such as the singleton scope. The mock is added to the module in the singleton scope:
void mock_module( di::registry& r ) { r.add( r .type<IModel>() .implementation<MockModel>() .in_scope<di::scopes::singleton>() ) ; }
Configuring the injector
The injector is the container of bindings that gets configured by adding modules:
di::injector inj; inj.install( dicpp_module ); inj.install( mock_module );
Configuring the mock
With the modules “installed” in the injector, the singleton mock instance can be obtained:
auto mock_model = inj.construct_ptr< IModel >(); MockModel* mock_model_ptr = dynamic_cast< MockModel* >(mock_model.get()); ASSERT_TRUE( mock_model_ptr ); EXPECT_CALL(*mock_model_ptr, Get()) .Times(AtLeast(1)) .WillRepeatedly(Return("{ \"a\" : 1 , \"b\" : 2 }"));
Logging in dicpp
An interesting feature in dicpp is logging of the library activity. The feature can be overridden or turned off via macro definition. The construction of dependencies can be traced, such as here, slightly shortened :
[DICPP]: Registering: IGetKeyValue with implementation: DicppJsonDecoder in scope: di::scopes::no_scope [DICPP]: Registering: IModel with implementation: MockModel in scope: di::scopes::singleton [DICPP]: Registering: MockModel with implementation: MockModel in scope: di::scopes::no_scope [DICPP]: Constructing: di::type_key<IModel, void> [DICPP]: Provided type: di::type_key<IModel, void> [DICPP]: Singleton: constructing: di::type_key<IModel, void> [DICPP]: Generic constructing IModel with implementation: MockModel [DICPP]: Completed constructing: di::type_key<IModel, void> with address: 0x7fd0b2600e40 ... [DICPP]: Singleton: returning existing: di::type_key<IModel, void>
As one can see, two bindings are registered for one implementation – the interface and implementation can be resolved at later time.
Summary
The rest of the example is very similar to the first and the second parts of the quest:
auto renderer = inj.construct_ptr< IRender >(); ASSERT_EQ( "a,b", renderer->Render() );
Although the library is very similar to sauce, the binding definition language doesn’t read as fluently as that of wallaroo or sauce. Further articles may elaborate on the outstanding features of either library.
Source: https://github.com/d-led/test-ioc-cpp
Updates
05.11.2014: Removed boost<->std shared_ptr
conversions, as dicpp has been updated in the meanwhile
More to come…
Pingback: Dependency Injection Libraries in C++. Part 2: sauce introduction
Pingback: Dependency Injection Libraries in C++. Part 1: Wallaroo, Introduction