免责声明;我知道这并不漂亮...
因此,我希望为各种不同类(子系统,管理器等(的全局,共享,实例创建一个注册表或共享对象存储。我不想为此使用单例,我希望能够拥有一个简单的注册表,我可以在其中根据密钥注册任何实例,然后稍后获取对该实例的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);
}
};