-
-
Save ArnCarveris/88ed2c91d6201995c7c0470ebe474662 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <cassert> | |
#include <iostream> | |
#include <functional> | |
#include <memory> | |
#include <type_traits> | |
#include <typeinfo> | |
#include <typeindex> | |
#include <utility> | |
#include <unordered_map> | |
// ***** IMPLEMENTATION **** | |
struct base_instance_container | |
{ | |
virtual ~base_instance_container() = default; | |
}; | |
template <class T> | |
struct instance_container : base_instance_container | |
{ | |
instance_container(std::unique_ptr<T> p) | |
: p(std::move(p)) | |
{ | |
} | |
std::unique_ptr<T> p; | |
}; | |
struct dependency_injector; | |
template <class T> | |
struct generic_argument | |
{ | |
template < | |
class Type, | |
std::enable_if_t< | |
!std::is_same<T, std::decay_t<Type>>::value, | |
int > = 0 | |
> | |
constexpr operator Type&() const noexcept { | |
return injector_. template create<Type>(); | |
} | |
dependency_injector& injector_; | |
}; | |
template <class T, class... arguments> | |
constexpr auto create_impl(dependency_injector& di) | |
-> std::enable_if_t<std::is_constructible<T, arguments...>::value, std::unique_ptr<instance_container<T>>> | |
{ | |
return std::make_unique<instance_container<T>>(std::make_unique<T>(arguments{di}...)); | |
} | |
template <class T, class... arguments> | |
constexpr auto create_impl(dependency_injector& di) | |
-> std::enable_if_t<!std::is_constructible<T, arguments...>::value, std::unique_ptr<instance_container<T>>> | |
{ | |
return create_impl<T, arguments..., generic_argument<T>>(di); | |
} | |
struct dependency_injector | |
{ | |
template <class T> | |
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<instance_container<T>*>(it->second.get())->p; | |
} else { | |
std::cout << "Instance does not exist, creating it for you: " << typeid(T).name() << std::endl; | |
return do_create<T>(); | |
} | |
} | |
template <class Interface, class Concret> | |
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<Concret>(*this)); | |
}); | |
} | |
private: | |
template <class T, std::enable_if_t<!std::is_abstract<T>::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<instance_container<T>*>(instances_[std::type_index{typeid(T)}].get())->p; | |
} else { | |
auto result = instances_.emplace(typeindex, create_impl<T>(*this)); | |
std::cout << "Instance created manually: " << typeid(T).name() << std::endl; | |
return *static_cast<instance_container<T>*>(result.first->second.get())->p; | |
} | |
} | |
// Abstract types need a special treatment to avoid making create_impl crazy and detecting interfaces without concret classes... | |
template <class T, std::enable_if_t<std::is_abstract<T>::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<instance_container<T>*>(instances_[std::type_index{typeid(T)}].get())->p; | |
} else { | |
assert(false && "No concret class provided"); | |
} | |
} | |
std::unordered_map<std::type_index, std::function<void()>> creator_; | |
std::unordered_map<std::type_index, std::unique_ptr<base_instance_container>> 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<Interface, Concret>(); | |
auto& c = di.create<C>(); | |
c.SayHi(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment