Last active
June 13, 2022 05:51
-
-
Save IwakuraRein/d19a9b55143105fed8aafe7935122d3b to your computer and use it in GitHub Desktop.
Simple resource manager in CPP
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include <string> | |
#include <map> | |
#include <unordered_map> | |
#include <memory> | |
#include <iostream> | |
#include <list> | |
#include <typeinfo> | |
using ResId = size_t; | |
static constexpr ResId ERROR_RES_ID = 18446744073709551615; | |
class ResourceBase { | |
public: | |
ResourceBase(const std::string& typeName) : _typeName{ typeName } {} | |
ResourceBase(const ResourceBase&&) = delete; | |
ResourceBase& operator=(const ResourceBase&&) = delete; | |
size_t size() const { return _baseMap.size(); } | |
ResId push(const std::string& name, std::shared_ptr<void> pRes) { | |
if (_name2id.count(name) != 0) { | |
std::cerr << "Error: " << _typeName << ": " << name << " already exists." << std::endl; | |
return ERROR_RES_ID; | |
} | |
_name2id.emplace(name, _nextId); | |
_id2name.emplace(_nextId, name); | |
_baseMap.emplace(_nextId, pRes); | |
return _nextId++; | |
} | |
bool exist(const ResId& id) const { if (id >= _nextId or _deleted.count(id) !=0 ) return false; return true; } | |
bool exist(const std::string& name) const { return _name2id.count(name) != 0; } | |
std::string name(const ResId& id) const { return _id2name.at(id); } | |
void remove(const ResId& id) { | |
if (exist(id)) { | |
_baseMap[id].reset(); | |
_deleted.emplace(id, id); | |
_name2id.erase(_id2name[id]); | |
_id2name.erase(id); | |
return; | |
} | |
std::cerr << "Warning: " << _typeName << ": No. " << id << " isn't in storage." << std::endl; | |
} | |
void remove(const std::string& name) { | |
if (_name2id.count(name)!=0) { | |
remove(_name2id[name]); | |
} | |
std::cerr << "Warning: " << _typeName << ": " << name << " isn't in storage." << std::endl; | |
} | |
void reName(const ResId& id, const std::string& name) { | |
if (exist(id)) { | |
if (_name2id.count(name) != 0) { | |
std::cerr << "Error: " << _typeName << ": New name " << name << " already exists." << std::endl; | |
return; | |
} | |
_name2id.erase(_id2name[id]); | |
_name2id[name] = id; | |
_id2name[id] = name; | |
return; | |
} | |
std::cerr << "Warning: " << _typeName << ": No. " << id << " isn't in storage." << std::endl; | |
} | |
void reName(const std::string& oldName, const std::string& newName) { | |
if (_name2id.count(oldName) != 0) { | |
if (_name2id.count(newName) != 0) | |
reName(_name2id[oldName], newName); | |
else std::cerr << "Warning: " << newName << " already exists." << std::endl; | |
} | |
else std::cerr << "Warning: " << oldName << " isn't in storage." << std::endl; | |
} | |
void createCollect(size_t type) { | |
_collections.emplace(type, std::move(std::unordered_map<ResId, std::list<ResId>>{})); | |
} | |
std::list<ResId>& getCollect(size_t type, ResId id) { | |
return _collections[type][id]; | |
} | |
template<typename T> | |
friend class Resource; | |
friend class ResourceManager; | |
private: | |
std::string _typeName; | |
ResId _nextId{ 0 }; | |
std::unordered_map<ResId, std::shared_ptr<void>> _baseMap; | |
std::unordered_map<std::string, ResId> _name2id; | |
std::unordered_map<ResId, std::string> _id2name; | |
std::unordered_map<ResId, ResId> _deleted; | |
std::unordered_map<size_t, std::unordered_map<ResId, std::list<ResId>>> _collections; | |
std::unordered_map<ResId, std::unordered_map<size_t, std::list<ResId>>> _collected; | |
std::unordered_map<size_t, std::unordered_map<ResId, ResId>> _associations; | |
}; | |
template<typename T> | |
class Resource : public ResourceBase { | |
public: | |
Resource() : ResourceBase(typeid(T).name()) {}; | |
Resource(ResourceBase&&) = delete; | |
Resource(Resource&&) = delete; | |
Resource& operator=(const Resource&&) = delete; | |
Resource& operator=(const ResourceBase&&) = delete; | |
std::shared_ptr<T> operator[](const ResId& id) { | |
auto it = _baseMap.find(id); | |
if (it == _baseMap.end()) return nullptr; | |
return std::static_pointer_cast<T>(it->second); | |
} | |
std::shared_ptr<T> operator[](const std::string& name) { | |
auto it = _name2id.find(name); | |
if (it == _name2id.end()) return nullptr; | |
auto it2 = _baseMap.find(it->second); | |
return std::static_pointer_cast<T>(it2->second); | |
} | |
ResId push(const std::string& name, std::shared_ptr<T> pRes) { | |
return ResourceBase::push(name, std::static_pointer_cast<void>(std::move(pRes))); | |
} | |
template<typename TT> | |
void createCollect() { | |
if (MapHas(_collections, typeid(TT).hash_code())) { | |
std::cerr << "Warning: " << _typeName << ": Connection to resource " << typeid(TT).name() << "already created" << std::endl; | |
return; | |
} | |
ResourceBase::createCollect(typeid(TT).hash_code()); | |
} | |
template<typename TT> | |
std::list<ResId>& getCollect(ResId id) { | |
if (!MapHas(_collections, typeid(TT).hash_code())) { | |
std::cerr << "Error: " << _typeName << ": The connection to " << typeid(TT).name() << " isn't created yet." << std::endl; | |
} | |
return ResourceBase::getCollect(typeid(TT).hash_code(), id); | |
} | |
friend class ResourceManager; | |
}; | |
class ResourceManager { | |
public: | |
ResourceManager() = default; | |
ResourceManager(ResourceManager&&) = delete; | |
ResourceManager& operator=(const ResourceManager&&) = delete; | |
template<typename T> | |
void addResource() { | |
std::unique_ptr<ResourceBase> pRes(new Resource<T>); | |
_resources.emplace(typeid(T).hash_code(), std::move(pRes)); | |
} | |
template<typename T> | |
Resource<T>& T() const { | |
ResourceBase& res = *_resources.at(typeid(T).hash_code()); | |
return static_cast<Resource<T>&>(res); | |
} | |
template<typename T> | |
Resource<T>& getResource() const { | |
ResourceBase& res = *_resources.at(typeid(T).hash_code()); | |
return static_cast<Resource<T>&>(res); | |
} | |
template<typename T> | |
std::shared_ptr<T> get(ResId id) const { | |
Resource<T>& res = static_cast<Resource<T>&>(*_resources.at(typeid(T).hash_code())); | |
return res[id]; | |
} | |
template<typename T> | |
std::shared_ptr<T> get(const std::string& name) const { | |
Resource<T>& res = static_cast<Resource<T>&>(*_resources.at(typeid(T).hash_code())); | |
return res[name]; | |
} | |
template<typename T, typename TT> | |
std::list<ResId>& getCollect(ResId id) const { | |
Resource<T>& res = static_cast<Resource<T>&>(*_resources.at(typeid(T).hash_code())); | |
return res.getCollect<TT>(id); | |
} | |
template<typename T> | |
ResId push(const std::string& name, std::shared_ptr<T> pRes) const { | |
Resource<T>& res = static_cast<Resource<T>&>(*_resources.at(typeid(T).hash_code())); | |
return res.push(name, pRes); | |
} | |
template<typename T> | |
size_t size() const { | |
Resource<T>& res = static_cast<Resource<T>&>(*_resources.at(typeid(T).hash_code())); | |
return res.size(); | |
} | |
template<typename T> | |
bool exist(ResId id) const { | |
Resource<T>& res = static_cast<Resource<T>&>(*_resources.at(typeid(T).hash_code())); | |
return res.exist(id); | |
} | |
template<typename T> | |
bool exist(const std::string& name) const { | |
Resource<T>& res = static_cast<Resource<T>&>(*_resources.at(typeid(T).hash_code())); | |
return res.exist(name); | |
} | |
template<typename T, typename TT> | |
void createCollect() { | |
_collections.emplace(typeid(T).hash_code(), typeid(TT).hash_code()); | |
Resource<T>& res = static_cast<Resource<T>&>(*_resources.at(typeid(T).hash_code())); | |
res.createCollect<TT>(); | |
} | |
template<typename T, typename TT> | |
void addCollect(ResId id1, ResId id2) const { | |
ResourceBase& res1 = *_resources.at(typeid(T).hash_code()); | |
ResourceBase& res2 = *_resources.at(typeid(TT).hash_code()); | |
res1._collections[typeid(TT).hash_code()][id1].push_back(id2); | |
res2._collected[id2][typeid(T).hash_code()].push_back(id1); | |
} | |
template<typename T> | |
void removeItem(ResId id) const { | |
ResourceBase& res = *_resources.at(typeid(T).hash_code()); | |
res.remove(id); | |
size_t type = typeid(T).hash_code(); | |
for (auto& pair : res.collected) { | |
if (pair.first == id) { | |
for (auto& pair2 : pair.second) { | |
ResourceBase& res = *_resources.at(pair2.first); | |
for (ResId id2 : pair2.second) | |
res._collections[type][id2].remove(id); | |
} | |
} | |
} | |
} | |
template<typename T, typename TT> | |
void removeFromCollect(ResId id1, ResId id2) const { | |
ResourceBase& res1 = *_resources.at(typeid(T).hash_code()); | |
ResourceBase& res2 = *_resources.at(typeid(TT).hash_code()); | |
res1._collections[typeid(TT).hash_code()][id1].remove(id2); | |
res2._collected[id2][typeid(T).hash_code()].remove(id1); | |
} | |
private: | |
std::unordered_map<size_t, std::unique_ptr<ResourceBase>> _resources; | |
std::unordered_map<size_t, size_t> _collections; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment