I'm thinking of two solutions to create a resource manager for my SFML-Game. Both require templating (I'm new to templating), but I prefer one of those solutions, even if I don't really know how to make it work.
I'd like to do something like this:
std::map<string, class> resources;
template<class T> T getResource(std::string name){
return (T) resources.find(name);
}
In other words, I'd like to have all my resources stored in one class, and that there is only one function to get a resource, and this function should always return the type of the resource - So that, in order to load a resource, I can just do:
Texture texture = resourceManager.getResource("mytexture.png");
But I can't figure out how to get this working.
My second approach would be to template a class, and then instantiate multiple resource managers, one for each type of resource.
template<class Res> class ResourceManager {
std::map<string, Res> resources;
...
Res get(std::string name){
return resources.find(name); //Just to visualize. Of course there would be null checks etc in the real method
}
}
In my opinion, the second "solution" looks way cleaner code-wise, but the first one would be easier to use; And I could just make the methods static so I won't have to worry about decoupling.
I really don't know where I should put the instances of the managers from the second approach. Probably I would make the instances static and put them in my main class, but that just doesnt seem right.
While nice, it will get really messy because you cannot have different resource types in the same map. They don't have a common base class. You don't have that many different resources though, so I'd suggest you just spell them out.
Personally, I hate strings as identifiers because the compiler cannot find typos, so I'm using an enum:
enum ResourceIdentifier
{
// TEXTURES
LoadingScreenBackground,
FireAnimation,
SmokeAnimation,
FloorTile,
// ...
// FONTS
MainFont
};
class ResourceManager
{
private:
std::map<ResourceIdentifier, std::shared_ptr<sf::Texture>> m_Textures;
std::map<ResourceIdentifier, std::shared_ptr<sf::Font>> m_Fonts;
public:
std::shared_ptr<sf::Texture> LoadTexture(ResourceIdentifier id, const sf::String& file);
std::shared_ptr<sf::Font> LoadFont(ResourceIdentifier id, const sf::String& file);
std::shared_ptr<sf::Texture> GetTexture(ResourceIdentifier id) const;
std::shared_ptr<sf::Font> GetFont(ResourceIdentifier id) const;
};
The first approach won't work because you have to know the size of an object to store it in a container. This is why there is no such thing as a class
type.
What you could do if you want to use your first approach, is to store void*
pointers instead, whose size is known, and then have your function return this pointer casted to the right pointer type.
std::map<string, void*> resources;
template<class T> T* getResource(std::string name){
return static_cast<T*>(resources.find(name));
}
This is kind of dirty and not type safe at all, it will probably break, but it works as you would expect. Your second approach is probably best :-)