module; #include #include #include #include #include export module core.resource_manager; export namespace core { template concept IsResourceLoader = requires(ResourceLoaderType loader, const std::string& name) { { loader.load_resource(name) } -> std::same_as; }; template < typename ResourceIDType, typename ResourceType, IsResourceLoader ResourceLoaderType > class ResourceManager { // Resource loader ResourceLoaderType& resource_loader_; // Registry of loaded resources, array index is resource ID std::vector resource_registry_; // Mapping of all loaded resources from (file) name to resource ID std::map resource_name_map_; public: ResourceManager() = delete; explicit ResourceManager(ResourceLoaderType& resource_loader) : resource_loader_{resource_loader} {} // No copy or move operations ResourceManager(const ResourceManager&) = delete; ResourceManager& operator=(const ResourceManager&) = delete; ResourceManager(ResourceManager&&) = delete; ResourceManager& operator=(ResourceManager&&) = delete; ~ResourceManager() = default; /** * Adds a new resource to the registry and returns its ID. */ ResourceIDType add_resource(ResourceType&& resource) { // Add resource to end of vector resource_registry_.push_back(std::move(resource)); // Return the index of the newly added resource return static_cast(resource_registry_.size() - 1); } /** * Adds a new resource to the registry, associates the name with it and returns its ID. */ ResourceIDType add_resource(ResourceType&& resource, const std::string& name) { // Add resource ResourceIDType resource_id = add_resource(std::move(resource)); // Associate name with resource ID for future access resource_name_map_.emplace(name, resource_id); return resource_id; } /** * Gets the resource ID for a given resource name, or `std::nullopt` if the resource was not found. */ std::optional get_resource_id_by_name(const std::string& name) const { // Check if resource is already loaded if ( const auto search = resource_name_map_.find(name); search != resource_name_map_.end() ) { // Return resource ID return search->second; } return std::nullopt; } /** * Loads a resource (e.g. from a file) and adds it to the registry if it isn't loaded yet. * Returns the resource ID. */ ResourceIDType load_resource_by_name(const std::string& name) { // Check if resource is already loaded if ( auto resource_id = get_resource_id_by_name(name); resource_id.has_value() ) { return resource_id.value(); } // Load resource and add it to the registry return add_resource(resource_loader_.load_resource(name), name); } /** * Returns a reference to the resource with the given ID. * The reference is not guaranteed to be valid after adding new resources to the registry. * Assumes that the resource ID is valid. */ const ResourceType& get_resource(const ResourceIDType resource_id) const { assert(resource_id < resource_registry_.size()); return resource_registry_[resource_id]; } }; }