//This is the basic interface for resource manager
class IResourceManager
//This is a management type specific manager
template<class rT>
class IHashResourceManager : public IResourceManager
//This is a resource specific manager
class ITextureManager : public IHashResourceManager<ITexture>
🔒 Locked
resource manager design
Advertisement
Original Post
I am redesigning the way that resources are managed in my engine and I have a question. My current idea is to have every kind of resource inherit from IResource, one kind of resource is ITexture. Then I have split up my resoure manager into 3 classes: All resource managers inheit from IResourceManager and each resource has a pointer of this type so they can use the resource manager that created them. The second resource management template object basically specifies the storage mechanisms, in this case it is a hash-map, but I could create more - for example what about an IPooledResourceManager. The last object is the resource specific class with better functions to load etc. My problem is that the IHashResourceManager needs to know the type of resource it is managing so that it can create the correct type of hash table and also to return the correct type of resource from a function such as: rT GetResource (string Name); It is for this reason that I made the class a template that accept the resource type as a parameter (An object inheritin from IResource). But it occurs to me that I could pass any kind of object as a parameter, something really silly that doesn't have the correct functions that come from IResource. Is there any way to ensure that objects I pass inherit from IResource? Or is there a better way to set up my Resource Manager?
Well, it seems to me that this is all rather overcomplicated. (take everything i say with a grain of salt). I guess what i'm asking is: do you really need that functionality you've created with your class design? Sure it's nice and everything, but if all it really does is complicate the design, it's drawbacks outway it's benefits.
What i would do (and have done), is hard-code seperate lists for seperate resource types under the umbrella of one single manager. Considering the number of different resource types (few) and the fact that they all behave differently, i just hardcoded them into the manager. It's really not a big deal. So far, i'm happy with the design.
Basically, how it would work is, instead of calling
GetResource( name, type );
i would call
GetFont( name );
which would look up the font in a list of fonts. If i called GetTexture(), it would look up a texture in a list of textures. I keep each resource seperate from the others.
Again, since there are only like 5 things i really want to load, this is not a bad way to go.
What i would do (and have done), is hard-code seperate lists for seperate resource types under the umbrella of one single manager. Considering the number of different resource types (few) and the fact that they all behave differently, i just hardcoded them into the manager. It's really not a big deal. So far, i'm happy with the design.
Basically, how it would work is, instead of calling
GetResource( name, type );
i would call
GetFont( name );
which would look up the font in a list of fonts. If i called GetTexture(), it would look up a texture in a list of textures. I keep each resource seperate from the others.
Again, since there are only like 5 things i really want to load, this is not a bad way to go.
Quote:
Original post by dmatter
... Is there any way to ensure that objects I pass inherit from IResource? ...
No, not directly. However, if the parameter doesn't inherit from IResource, there will probably be errors when compiling.
Now, suppose that the parameter doesn't inherit from IResource, but it is similar enough that it still compiles. Is that really a problem? If it is, then it is probably because you are casting types (or doing implicit type conversion), and that is where the real problem lies.
leiavoia: That it close to what I'm using at the moment but now that I have the time I would like to improve the system to allow for more resource types to be added and managed more easily, as I have already done with my render system.
JohnBolton: I am not a fan of type casting, especially from a base class up the inheritance tree. This is partly why am considering the design mentioned in my previos poste... read on.
If anyone is familier with the Ogre engine. I have been looking at the API reference pages for its resource manager(s). Their system seems to store a map of resources and each resource-specific manager inherits from the ResourceManager. This seems to be fine, but what I have noticed is that if you try to get a class by its name or resourcehandle from any resource manager - for example a mesh from the mesh manager - then all you get back is a pointer to a resource (the base class), in order to get/use the functionality of what-ever resource is needed you must surely cast to the correct type, which as I understand (or was told) causes overhead and a properly designed system should be able to avoid this.
Another question I have is: Who shoud load the data from a file - the object itself or its manager? I think Ogre uses a serialiser, so I see that as a third option - the 'helper class'.
Many thanks so far,
David
JohnBolton: I am not a fan of type casting, especially from a base class up the inheritance tree. This is partly why am considering the design mentioned in my previos poste... read on.
If anyone is familier with the Ogre engine. I have been looking at the API reference pages for its resource manager(s). Their system seems to store a map of resources and each resource-specific manager inherits from the ResourceManager. This seems to be fine, but what I have noticed is that if you try to get a class by its name or resourcehandle from any resource manager - for example a mesh from the mesh manager - then all you get back is a pointer to a resource (the base class), in order to get/use the functionality of what-ever resource is needed you must surely cast to the correct type, which as I understand (or was told) causes overhead and a properly designed system should be able to avoid this.
Another question I have is: Who shoud load the data from a file - the object itself or its manager? I think Ogre uses a serialiser, so I see that as a third option - the 'helper class'.
Many thanks so far,
David
Quote:
Original post by dmatter
... Who shoud load the data from a file - the object itself or its manager? I think Ogre uses a serialiser, so I see that as a third option - the 'helper class'.
I strongly recommend the "helper" (factory) class. It loads the object's data and then passes it as parameters to the object's constructor. That way you can create the object from a variety of sources without putting all that code in the object itself.
How would I create such a helper class. I mean it doesn't have access to the private and protected members of any resource unless either it is a friend or there are Set_something_() functions for each data member. Is there a 'standard' way of doing this.
I am still interested in feedback for my resource manager design.
ta,
David
I am still interested in feedback for my resource manager design.
ta,
David
Quote:
Original post by dmatter
How would I create such a helper class.
Its a design pattern, google up "factory method" assuming C++ here is generic factory you can use its a simplified version of the loki's library for the sake of this discussion:
#include <exception>#include <map>template < typename AbstractProductPtr, typename IdentifierType, typename ProductCreator = AbstractProductPtr (*)(), typename Predicate = std::less< IdentifierType >, typename Alloc = std::allocator< std::pair<const IdentifierType, ProductCreator> >>struct Factory { typedef IdentifierType key_type; typedef ProductCreator mapped_type; typedef typename Alloc::template rebind< std::pair<const key_type, mapped_type> >::other fac_allocator; typedef std::map< key_type, mapped_type, Predicate, fac_allocator > IdToProductMap; typedef typename IdToProductMap::value_type value_type; struct UnknownTypeException : public std::exception { const char* what() const throw() { return "Unknown Type"; } };private: IdToProductMap associations_; public: bool registerProduct(const key_type& id, ProductCreator creator) { return associations_.insert(typename IdToProductMap::value_type(id, creator)).second; } bool unregisterProduct(const key_type& id) { return associations_.erase(id) == 1; } AbstractProductPtr create(const key_type& id) const { typename IdToProductMap::const_iterator i = associations_.find(id); if(i != associations_.end()) return (i->second)(); throw UnknownTypeException(); }};Example usage:
object.hpp
#include <string>struct Obj { virtual ~Obj() {} ....};typedef Factory< Obj*, std::string > ObjectFactory;extern ObjectFactory objFactory;....struct foo : public Obj {};Obj* createFoo();object.cpp
#include "object.hpp"ObjectFactory objFactory;Obj* createFoo() { return new foo(); }namespace { //anonymous namespaces creates an internal linkage const std::string FOO("foo"); const bool result = objFactory.registerProduct(FOO, createFoo);};main.cpp:
#include "object.hpp"int main() { Obj* o = objFactory.create("foo"); ..... delete o;}Its highly simplified and you probably wont see the benefits of it until you do some research & studying on design patterns and the factory method design pattern (lots of resources on the net).
Also you would normally make a factory a singleton another design pattern which out of all the patterns doesn't have many friends but thats another story [smile]
Another thing you can do to improve it is there is no need for ordering of elements so a hash map could be used instead which speeds up the look-up to amortized constant time, but currently the standard library doesn't have a hash map how-ever there is one in the standard library extensions and nearly all compilers come with an implementation, i'll leave that as an exercise for you to do.
Reading some of this thread i recommend reading up on smart pointers/handles, reference counting, and something very relevant the Proxy design pattern. Also have a look into the boost & loki libraries (links in my sig).
It's a tough problem to do "elegantly". Look at it this way though, at some point part of your program is eventually going to request a resource and that resource is going to be very specialized. You're going to have to know what kind of resource it is you're asking for and what to do with it once you get it. If you do abstractions then you'll have to do some casting once you get the object returned to you, so you're not really isolating the client from the implementation at all.
All of these resources will need to be stored seperately since they are so different and beacause it's pointless to to search through a bunch of sounds and game objects if all you want is to find a texture.
Therefore, I think it's much better to just bite the bullet and do some hard-coding. If you're using C++, just use templates. Maybe make each manager a singleton. Don't bother trying to unify them all under some overarching MANAGER class because you'll just wind up having a huge if/else or switch block cluttering up your code. And besides, you're not really simplifying your coders' lives. Is having a centralized object that you call GetResource(RES_TYPE_TEXTURE, "cool_tex") on better than calling GetTexture("tex_name") on your TextureManager object directly?
The only benefit of design patterns and abstractions is to protect the programmer, make their lives easier, and assist them down the line if implementations need to change. The implementations for these managers are unlikely to change. Texture managers will probably always use a hash table keyed by a string, for example.
However, having a base interface for texture resources IS helpful, because down the line you may switch to JPG instead of TGA, etc. So have your texture manager deal with ITexture.
All of these resources will need to be stored seperately since they are so different and beacause it's pointless to to search through a bunch of sounds and game objects if all you want is to find a texture.
Therefore, I think it's much better to just bite the bullet and do some hard-coding. If you're using C++, just use templates. Maybe make each manager a singleton. Don't bother trying to unify them all under some overarching MANAGER class because you'll just wind up having a huge if/else or switch block cluttering up your code. And besides, you're not really simplifying your coders' lives. Is having a centralized object that you call GetResource(RES_TYPE_TEXTURE, "cool_tex") on better than calling GetTexture("tex_name") on your TextureManager object directly?
The only benefit of design patterns and abstractions is to protect the programmer, make their lives easier, and assist them down the line if implementations need to change. The implementations for these managers are unlikely to change. Texture managers will probably always use a hash table keyed by a string, for example.
However, having a base interface for texture resources IS helpful, because down the line you may switch to JPG instead of TGA, etc. So have your texture manager deal with ITexture.
Quote:
Original post by dmatter
How would I create such a helper class. I mean it doesn't have access to the private and protected members...
Why would it need access to private data in the resource? Just load all the data and pass it to the constructor through the parameters when creating the resource. There are some drawbacks to doing this, but they are mostly optimization issues.
For example, a constructor (simplified) for an image resource:
ImageResource::ImageResource( int width, int height, int bitsPerPixel, char const * pImage ){ ...} Now, the factory simply loads the image data out of the BMP, TGA, TIFF, JPG, or whatever kind of file, massages it into a more generic form, and finally does something like this: IResource * pResource = new ImageResource( width, height, bitsPerPixel, pImage );
Topic Locked
This topic has been locked by a moderator. New replies are not allowed.
Sign in to reply to this topic.
Advertisement