问题
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.
回答1:
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;
};
回答2:
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 :-)
来源:https://stackoverflow.com/questions/27697598/using-c-templating-for-sfml-resource-manager