Skip to content

Instantly share code, notes, and snippets.

@ArnCarveris
Forked from Jiwan/tiny-di.cpp
Created January 2, 2024 00:16
Show Gist options
  • Save ArnCarveris/88ed2c91d6201995c7c0470ebe474662 to your computer and use it in GitHub Desktop.
Save ArnCarveris/88ed2c91d6201995c7c0470ebe474662 to your computer and use it in GitHub Desktop.
#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