将shared_ptr与通用注册表或共享对象存储一起使用.或不



免责声明;我知道这并不漂亮...

因此,我希望为各种不同类(子系统,管理器等(的全局,共享,实例创建一个注册表或共享对象存储。我不想为此使用单例,我希望能够拥有一个简单的注册表,我可以在其中根据密钥注册任何实例,然后稍后获取对该实例的shared_ptr。

通过使用可疑的强制转换、原始字节视图和暗中强制,我设法创建了一个可以做到这一点并且有效的类(至少在 VS 2015 中(。

我可以做这样的事情

myRegistry->add<FooType>(1,2,3);
....
auto shared_foo_ptr = myRegistry->get<FooType>();

这很可爱,但引擎盖下的代码当然有味道。

还是吗?

在这里,我真的很想得到一些评论和建设性的撕成碎片;

 class Registry {
    struct reg_shared_ptr_item {
        // raw byte storage for a shared_ptr of any type (assuming they are always the same size)
        char _storage[sizeof(std::shared_ptr<reg_shared_ptr_item>)];
        // the deleter will be bound to the correctly typed destructor of the original share_ptr
        std::function<void(reg_shared_ptr_item&)> _deleter;
    };
    /**
    this union is used to get access to the raw bytes of a pointer-to-member 
    since these can't be cast to anything directly
    **/
    union fptr_hasher {
        char _raw[sizeof(&std::shared_ptr<Registry>::use_count)];
        uint64_t _key_bits;
    };
    template<typename T>
    static uint32_t get_key_for_type() {
        // C++ disallows converting a pointer-to-member to a void* or any other normal pointer
        // but we don't really need that, we just need the bits which will be unique since each type 
        // has it's own implementation
        // NOTE: if the compiler for some reason or other chooses to be clever about instantiation and 
        //       it doesn't actually create a new instance for each type then this will cause collisions
        typedef long (std::shared_ptr<T>::*fptr_t)(void) const;
        fptr_hasher hasher;
        // create a pointer-to-member instance over the union so that we get access to the raw bytes
        fptr_t* fptr = ::new(&hasher) fptr_t;
        *fptr = &std::shared_ptr<T>::use_count;
        return (reg_key_t)hasher._key_bits;
    }
public:
    typedef uint32_t reg_key_t;
    Registry() = default;
    ~Registry() {
        // clean up; invoke the type-bound destructors
        for (auto& kv : _reg_map) {         
           kv.second._deleter(kv.second);
        }
    }
    template<typename T, class...Args>
    bool add(Args&&...args) {
        auto key = get_key_for_type<T>();
        auto found = _reg_map.find(key);
        if (found == _reg_map.end()) {
            // first; initialise the raw memory location to be a proper shared_ptr
            reg_shared_ptr_item entry;
            ::new(entry._storage) std::shared_ptr<T>();
            // create-assign a new shared_ptr instance
            auto sas = reinterpret_cast<std::shared_ptr<T>*>(entry._storage);
            *sas = std::make_shared<T>(args...); //< at this point the instance count is 1
            // store off f-pointer to a properly typed destructor           
            entry._deleter = [](reg_shared_ptr_item& item) {
                reinterpret_cast<std::shared_ptr<T>*>(item._storage)->~shared_ptr<T>();
            };
            _reg_map.emplace(key,entry);
            return true;
        }
        return false;
    }
    template<typename T>
    std::shared_ptr<T> get() const {
        auto key = get_key_for_type<T>();
        auto found = _reg_map.find(key);
        if (found != _reg_map.end()) {
            return *(reinterpret_cast<std::shared_ptr<T>*>(const_cast<char*>(found->second._storage)));
        }
        return nullptr;
    }
    template<typename T>
    static reg_key_t getHash() {
        return get_key_for_type<T>();
    }
private:        
    typedef std::map<reg_key_t, reg_shared_ptr_item> reg_map_t;
    reg_map_t _reg_map;
};
您可以使用

std::type_index作为键,shared_ptr<void>作为类型擦除的存储。也许是这样的(演示(:

class Registry {
    std::map<std::type_index, std::shared_ptr<void> > registry_;
public:
    template<typename T, class...Args>
    bool add(Args&&...args) {
        std::type_index key(typeid(T));
        if (!registry_.count(key)) {
            auto p = std::make_shared<T>(std::forward<Args>(args)...);
            registry_[key] = p;
            return true;
        }
        return false;
    }
    template<typename T>
    std::shared_ptr<T> get() const {
        auto it = registry_.find(typeid(T));
        if (it == registry_.end()) {
            return std::shared_ptr<T>();
        }
        return std::static_pointer_cast<T>(it->second);
    }
};

最新更新