C++17 inversion of control and static dependency injection library.
- Constant, dynamic, and automatic resolvers
- Singleton, resolution (TODO), and unique scopes
Scope manages the uniqueness of a dependency.
Singleton scopes are cached after the first resolution and will be returned on subsequent inversify::get...
calls.
Resolution scopes are cached throughout the duration of a single inversify::get...
call. A dependency tree with duplicate dependencies will resolve each to the same cached value.
By default, the unique scope is used (except for constant values). The unique scope will resolve a unique dependency for each inversify::get...
call.
#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 symbols {
using foo = inversify::Symbol<int>;
using bar = inversify::Symbol<double>;
using fizz = inversify::Symbol<IFizzPtr>;
using fizzFactory = inversify::Symbol<std::function<IFizzPtr()>>;
using autoFizzUnique = inversify::Symbol<std::function<IFizzUniquePtr>>;
using autoFizzShared = inversify::Symbol<std::function<IFizzSharedPtr>>;
}
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 };
};
template <>
struct inversify::Injectable<Fizz>
: inversify::Inject<
symbols::foo,
symbols::bar
>
{ };
Constant bindings are always singletons.
inversify::bind<symbols::foo>().toConstantValue(10);
inversify::bind<symbols::bar>().toConstantValue(1.618);
Dynamic bindings are resolved when calling inversify::get...
.
By default, dynamic bindings have resolution scope (e.g. each call to inversify::get...
calls the factory).
Singleton scope dynamic bindings cache the first resolution of the binding.
inversify::bind<symbols::fizz>().toDynamicValue(
[](const inversify::Context& ctx) {
auto foo = inversify::get<symbols::foo>();
auto bar = inversify::get<symbols::bar>();
auto fizz = std::make_shared<Fizz>(foo, bar);
return fizz;
}
).inSingletonScope();
Dynamic bindings can be used to resolve factory functions.
inversify::bind<symbols::fizzFactory>().toDynamicValue(
[]() {
return [](const inversify::Context& ctx) {
auto foo = inversify::get<symbols::foo>();
auto bar = inversify::get<symbols::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.
inversify::bind<symbols::autoFizzUnique>().to<Fizz>();
inversify::bind<symbols::autoFizzShared>().to<Fizz>().inSingletonScope();
auto bar = inversify::get<symbols::bar>();
auto fizz = inversify::get<symbols::fizz>();
fizz->buzz();
auto fizzFactory = inversify::get<symbols::fizzFactory>();
auto anotherFizz = fizzFactory();
anotherFizz->buzz();
auto autoFizzUnique = inversify::get<symbols::autoFizzUnique>();
autoFizzUnique->buzz();
auto autoFizzShared = inversify::get<symbols::autoFizzShared>();
autoFizzShared->buzz();
Use the following to run tests:
bazel run test --enable_platform_specific_config
Note: run the example app in a similar way:
bazel run example --enable_platform_specific_config
Run python ./third_party/amalgamate/amalgamate.py -c ./third_party/amalgamate/config.json -s ./
from the root of the repository.
- InversifyJS - API design and inspiration for the project.