C++17 inversion of control and dependency injection container library.
- Constant, dynamic, and factory resolvers
- Singleton and per-resolution scopes
#include <mosure/inversify.hpp>
// for convenience
namespace inversify = mosure::inversify;
struct IFizz {
virtual ~IFizz() = default;
virtual void buzz() = 0;
};
using IFizzPtr = std::unique_ptr<IFizz>;
namespace types {
inline const inversify::Symbol foo { "Foo" };
inline const inversify::Symbol bar { "Bar" };
inline const inversify::Symbol fizz { "Fizz" };
inline const inversify::Symbol fizzFactory { "FizzFactory" };
inline const inversify::Symbol autoFizz { "AutoFizz" };
inline const inversify::Symbol autoFizzUnique { "AutoFizzUnique" };
inline const inversify::Symbol autoFizzShared { "AutoFizzShared" };
}
struct Fizz : IFizz {
Fizz(int foo, double bar)
:
foo_(foo),
bar_(bar)
{ }
void buzz() override {
std::cout << "Fizz::buzz() - foo: " << foo_
<< " - bar: " << bar_
<< " - counter: " << ++counter_
<< std::endl;
}
int foo_;
int bar_;
int counter_ { 0 };
};
inline static auto injectFizz = inversify::Injectable<Fizz>::inject(
inversify::Inject<int>(types::foo),
inversify::Inject<double>(types::bar)
);
inversify::Container container {};
Constant bindings are always singletons.
container.bind<int>(types::foo)->toConstantValue(10);
container.bind<double>(types::bar)->toConstantValue(1.618);
Dynamic bindings are resolved when calling container.get....
By default, dynamic bindings have resolution scope (e.g. each call to container.get... calls the factory).
Singleton scope dynamic bindings cache the first resolution of the binding.
container.bind<IFizzPtr>(types::fizz)->toDynamicValue(
[](const inversify::Context& ctx) {
auto foo = ctx.container.get<int>(types::foo);
auto bar = ctx.container.get<double>(types::bar);
auto fizz = std::make_shared<Fizz>(foo, bar);
return fizz;
}
)->inSingletonScope();
Dynamic bindings can be used to resolve factory functions.
container.bind<std::function<IFizzPtr()>>(types::fizzFactory)->toDynamicValue(
[](const inversify::Context& ctx) {
return [&]() {
auto foo = ctx.container.get<int>(types::foo);
auto bar = ctx.container.get<double>(types::bar);
auto fizz = std::make_shared<Fizz>(foo, bar);
return fizz;
};
}
);
Dependencies can be resolved automatically using an automatic binding. Injectables are a prerequisite to the type being constructed.
Automatic bindings can generate instances, unique_ptr's, and shared_ptr's of a class. The returned type is determined by the binding interface.
container.bind<Fizz>(types::autoFizz)->to<Fizz>();
container.bind<IFizzUniquePtr>(types::autoFizzUnique)->to<Fizz>();
container.bind<IFizzSharedPtr>(types::autoFizzShared)->to<Fizz>()->inSingletonScope();
auto bar = container.get<double>(types::bar);
auto fizz = container.get<IFizzPtr>(types::fizz);
fizz->buzz();
auto fizzFactory = container.get<std::function<IFizzPtr()>>(types::fizzFactory);
auto anotherFizz = fizzFactory();
anotherFizz->buzz();
auto autoFizz = container.get<Fizz>(types::autoFizz);
autoFizz.buzz();
auto autoFizzUnique = container.get<IFizzUniquePtr>(types::autoFizzUnique);
autoFizzUnique->buzz();
auto autoFizzShared = container.get<IFizzSharedPtr>(types::autoFizzShared);
autoFizzShared->buzz();
Use the following to run tests:
bazel run test --enable_platform_specific_config
- More compile-time checks
- Thread safety
Run python ./third_party/amalgamate/amalgamate.py -c ./third_party/amalgamate/config.json -s ./ from the root of the repository.