Program Listing for File linker.h

↰ Return to documentation for file (PrismEngine/src/linker.h)

#pragma once
#include <memory>
#include <typeindex>
#include <utility>
#include <vector>
#include <unordered_map>
namespace prism {
    class Linker {
    public:
        Linker() = default;
        ~Linker();

        template<typename A, typename B>
        void link(A* a, B* b) {
            if (!a || !b) return;

            if (find<A, B>(a) != nullptr || find<B, A>(b) != nullptr) {
                return;
            }

            insert_(a, typeid(B), b);
            insert_(b, typeid(A), a);
        }

        template<typename A, typename B>
        void unlink(A* a, B* b) {
            if (!a || !b) return;
            erase_(a, typeid(B), b);
            erase_(b, typeid(A), a);
        }

        template<typename T>
        void detach(T* obj) {
            if (!obj) return;

            std::vector<std::pair<void*, std::type_index>> targets;
            targets.reserve(map_.size() / 2);

            for (auto it = map_.begin(); it != map_.end(); ) {
                if (it->first.ptr == obj) {
                    targets.emplace_back(it->second, it->first.target_type);
                    it = map_.erase(it);
                }
                else {
                    ++it;
                }
            }

            for (const auto& p : targets) {
                erase_(p.first, typeid(T), obj);
            }
        }

        template<typename A, typename B>
        B* find(A* a) {
            if (!a) return nullptr;

            auto it = map_.find(Key{ a, typeid(B) });
            if (it != map_.end()) {
                return static_cast<B*>(it->second);
            }
            return nullptr;
        }

        void clear();


    private:
        struct Key {
            void* ptr;
            std::type_index target_type;

            Key(void* p, std::type_index t) : ptr(p), target_type(t) {}

            bool operator==(const Key& other) const {
                return ptr == other.ptr && target_type == other.target_type;
            }
        };

        struct KeyHash {
            std::size_t operator()(const Key& k) const {
                return std::hash<void*>()(k.ptr) ^
                    (std::hash<std::type_index>()(k.target_type) << 1);
            }
        };

        std::unordered_map<Key, void*, KeyHash> map_;

        void insert_(void* source, std::type_index target_type, void* target);
        void erase_(void* source, std::type_index target_type, void* expected_target);
    };

    extern Linker linker;
}