Skip to content

Instantly share code, notes, and snippets.

@IwakuraRein
Last active June 13, 2022 05:51
Show Gist options
  • Save IwakuraRein/d19a9b55143105fed8aafe7935122d3b to your computer and use it in GitHub Desktop.
Save IwakuraRein/d19a9b55143105fed8aafe7935122d3b to your computer and use it in GitHub Desktop.
Simple resource manager in CPP
#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