Untitled
unknown
c_cpp
10 months ago
4.0 kB
17
Indexable
#pragma once
#include <limits>
#include <concepts>
#include <memory>
#include <unordered_map>
#include <vector>
#include <typeindex>
#include <stdexcept>
#include <any>
#include "rrc/Resource.hpp"
namespace rrc
{
template<typename From, typename To, typename... Args>
const To& convert(From&, To&, Args&&...);
// Special tag to represent unknown resource type
class Any
{
};
template<typename T>
concept ResourceType = std::derived_from<T, Resource>;
template<typename T>
concept ResourceOrAnyType = std::same_as<T, Any> || ResourceType<T>;
using ResourceId = uint32_t;
inline constexpr ResourceId InvalidId = std::numeric_limits<ResourceId>::max();
using Hash = size_t;
template <ResourceOrAnyType ResAny>
struct ResourceHandle
{
ResourceId id = InvalidId;
bool valid() const { return id != InvalidId; }
explicit operator bool() const { return valid(); }
operator ResourceHandle<Any>() const { return { id }; }
};
// TODO: const before T&
template <typename T>
concept HasHash = requires(T& t) {
{ t.hash() } -> std::convertible_to<std::size_t>;
};
class Registry
{
public:
template <ResourceType Res, typename... Args>
ResourceHandle<Res> load(Args&&... args)
{
auto resource = std::make_unique<Res>(std::forward<Args>(args)...);
if constexpr (Res::cachable)
{
static_assert(HasHash<Res>, "Cacheable resources must implement hash()");
Hash h = resource->hash();
auto it = cache_.find(h);
if (it != cache_.end())
{
return makeHandle<Res>(it->second);
}
ResourceId id = nextId_++;
cache_[h] = id;
storage_[id] = std::move(resource);
return makeHandle<Res>(id);
}
else
{
ResourceId id = nextId_++;
storage_[id] = std::move(resource);
return makeHandle<Res>(id);
}
}
template <ResourceType Res>
void remove(ResourceHandle<Res> handle)
{
// Remove from storage
storage_.erase(handle.id);
// Remove from cache if applicable
if constexpr (Res::cachable)
{
for (auto it = cache_.begin(); it != cache_.end(); )
{
if (it->second == handle.id)
{
it = cache_.erase(it);
}
else {
it++;
}
}
}
}
template <typename Rep, ResourceOrAnyType ResAny>
Rep& get(ResourceHandle<ResAny> handle)
{
Resource* resource = resolveHandle(handle);
if constexpr (!std::same_as<ResAny, Any>)
{
if (typeid(*resource) != typeid(ResAny))
{
throw std::runtime_error("ResourceHandle does not match requested type");
}
}
return resource->template get<Rep>();
}
template<typename From, typename To, ResourceType Res, typename... Args>
const To& convert(ResourceHandle<Res> handle, Args&&... args)
{
Resource* resource = resolveHandle(handle);
if (typeid(*resource) != typeid(Res))
{
throw std::runtime_error("ResourceHandle does not point to correct type");
}
using rrc::convert;
return convert(
resource->template get<From>(),
resource->template get<To>(),
std::forward<Args>(args)...
);
}
// Type check for Any handles
template <ResourceType Res>
bool is(ResourceHandle<Any> handle)
{
Resource* res = resolveHandle(handle);
return res && typeid(*res) == typeid(Res);
}
private:
template <ResourceType Res>
ResourceHandle<Res> makeHandle(ResourceId id)
{
return ResourceHandle<Res>{ id };
}
template <ResourceOrAnyType ResAny>
Resource* resolveHandle(ResourceHandle<ResAny> handle)
{
auto it = storage_.find(handle.id);
if (it != storage_.end())
{
return it->second.get();
}
throw std::runtime_error("Invalid resource handle");
}
std::unordered_map<ResourceId, std::unique_ptr<Resource>> storage_;
std::unordered_map<Hash, ResourceId> cache_;
ResourceId nextId_ = 0;
};
}Editor is loading...
Leave a Comment