Originally Posted by
CornedBee
This assumption is the reason why singletons are used. But simple parameter passing and/or dependency injection make for better program structure IMO. So instead of being able to procure a resource manager anywhere, components that need one will need to have it passed in by whoever created them.
Your unit tests will thank you.
I agree, the idea of a singleton wasn't appealing. But since i might have quite a few objects and methods that need to access resources, simply passing around the resource manager isn't ideal either.
Here is what i ended up doing:
Code:
#ifndef RESOURCE_HPP
#define RESOURCE_HPP
#include <string>
#include <map>
#include <memory>
template <class ResourceType>
class Resource
{
public:
template <class ResourceLoader>
Resource(const std::string &p_path, ResourceLoader p_resourceloader) : m_path(p_path)
{
if(!m_resource_cache.Contains(m_path))
{
m_resource_cache.AddResource(m_path, p_resourceloader(m_path));
}
}
ResourceType& Get() const
{
return m_resource_cache.GetResource(m_path);
}
private:
std::string m_path;
class ResourceCache
{
public:
void AddResource(const std::string &p_path, std::unique_ptr<ResourceType> p_resource)
{
m_resourcemap[p_path] = std::move(p_resource);
}
ResourceType& GetResource(const std::string &p_path) const
{
return *(m_resourcemap.at(p_path));
}
bool Contains(const std::string &p_path) const
{
return (m_resourcemap.end() != m_resourcemap.find(p_path));
}
private:
std::map<std::string, std::unique_ptr<ResourceType> > m_resourcemap;
};
static ResourceCache m_resource_cache;
};
template<class ResourceType>
typename Resource<ResourceType>::ResourceCache Resource<ResourceType>::m_resource_cache;
#endif
It's a slight variant of the third pattern i mentioned in the OP. To use it you need simply supply a path to whatever resource you want to load, and a functor/function that does the actual loading:
Code:
std::unique_ptr<sf::Texture> LoadTexture(const std::string &p_path)
{
std::unique_ptr<sf::Texture> texture(new sf::Texture);
if(!texture->loadFromFile(p_path))
throw std::runtime_error("Unable to load texture: " + p_path);
return texture;
}
.....
Resource<sf::Texture> texture(path, LoadTexture);
FunctionThatNeedsTexture(texture.get());
It's working out quite well so far, any thoughts?