Skip to content

Instantly share code, notes, and snippets.

@Jiwan
Last active January 2, 2024 00:16
Show Gist options
  • Save Jiwan/aefcb797c31911377b1c1c679ca5f1cd to your computer and use it in GitHub Desktop.
Save Jiwan/aefcb797c31911377b1c1c679ca5f1cd to your computer and use it in GitHub Desktop.

Revisions

  1. Jiwan revised this gist May 31, 2019. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion tiny-di.cpp
    Original file line number Diff line number Diff line change
    @@ -153,7 +153,7 @@ struct Concret : Interface

    struct C
    {
    C(B& b, Interface& i, A& a) : i(i) {}
    C(B& b, Interface& i, A& a) : i(i) {}

    void SayHi() {
    i.SayHi();
  2. Jiwan revised this gist May 31, 2019. 1 changed file with 5 additions and 0 deletions.
    5 changes: 5 additions & 0 deletions tiny-di.cpp
    Original file line number Diff line number Diff line change
    @@ -8,6 +8,9 @@
    #include <utility>
    #include <unordered_map>


    // ***** IMPLEMENTATION ****

    struct base_instance_container
    {
    virtual ~base_instance_container() = default;
    @@ -121,6 +124,8 @@ struct dependency_injector
    std::unordered_map<std::type_index, std::unique_ptr<base_instance_container>> instances_;
    };

    // ***** TEST SCENARIO ****

    struct A
    {
    };
  3. Jiwan created this gist May 31, 2019.
    167 changes: 167 additions & 0 deletions tiny-di.cpp
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,167 @@
    #include <cassert>
    #include <iostream>
    #include <functional>
    #include <memory>
    #include <type_traits>
    #include <typeinfo>
    #include <typeindex>
    #include <utility>
    #include <unordered_map>

    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_;
    };

    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();
    }