#include #include #include #include #include #include #include #include #include // ***** IMPLEMENTATION **** struct base_instance_container { virtual ~base_instance_container() = default; }; template struct instance_container : base_instance_container { instance_container(std::unique_ptr p) : p(std::move(p)) { } std::unique_ptr p; }; struct dependency_injector; template struct generic_argument { template < class Type, std::enable_if_t< !std::is_same>::value, int > = 0 > constexpr operator Type&() const noexcept { return injector_. template create(); } dependency_injector& injector_; }; template constexpr auto create_impl(dependency_injector& di) -> std::enable_if_t::value, std::unique_ptr>> { return std::make_unique>(std::make_unique(arguments{di}...)); } template constexpr auto create_impl(dependency_injector& di) -> std::enable_if_t::value, std::unique_ptr>> { return create_impl>(di); } struct dependency_injector { template T& create() { // Check if there is already an instance of that type std::cout << "Querying a instance of type: " << typeid(T).name() << std::endl; auto typeindex = std::type_index{typeid(T)}; auto it = instances_.find(typeindex); if (it != instances_.end()) { std::cout << "Instance already existing: " << typeid(T).name() << std::endl; return *static_cast*>(it->second.get())->p; } else { std::cout << "Instance does not exist, creating it for you: " << typeid(T).name() << std::endl; return do_create(); } } template void implement() { creator_.emplace(std::type_index{typeid(Interface)}, [this]() { std::cout << "Creating " << typeid(Interface).name() << " with concret type: " << typeid(Concret).name() << std::endl; instances_.emplace(std::type_index{typeid(Interface)}, create_impl(*this)); }); } private: template ::value, int> = 0> T& do_create() { auto typeindex = std::type_index{typeid(T)}; auto creator_it = creator_.find(std::type_index{typeid(T)}); if (creator_it != creator_.end()) { std::cout << "Instance created using a creator: " << typeid(T).name() << std::endl; creator_it->second(); return *static_cast*>(instances_[std::type_index{typeid(T)}].get())->p; } else { auto result = instances_.emplace(typeindex, create_impl(*this)); std::cout << "Instance created manually: " << typeid(T).name() << std::endl; return *static_cast*>(result.first->second.get())->p; } } // Abstract types need a special treatment to avoid making create_impl crazy and detecting interfaces without concret classes... template ::value, int> = 0> T& do_create() { auto typeindex = std::type_index{typeid(T)}; auto creator_it = creator_.find(std::type_index{typeid(T)}); if (creator_it != creator_.end()) { std::cout << "Instance created using a creator: " << typeid(T).name() << std::endl; creator_it->second(); return *static_cast*>(instances_[std::type_index{typeid(T)}].get())->p; } else { assert(false && "No concret class provided"); } } std::unordered_map> creator_; std::unordered_map> instances_; }; // ***** TEST SCENARIO **** struct A { }; struct B { }; struct Interface { virtual ~Interface() = default; virtual void SayHi() = 0; }; struct Concret : Interface { Concret(A& a) {} void SayHi() override { std::cout << "Hi" << std::endl; } }; struct C { C(B& b, Interface& i, A& a) : i(i) {} void SayHi() { i.SayHi(); } Interface& i; }; int main() { dependency_injector di; di.implement(); auto& c = di.create(); c.SayHi(); }