This topic is killing me, I want it to be simple as possible but not simpler, I can't just think of a way to do it.
Ill start with what I came to:
This is Reference counted interface, objects that wishes to be reference counted must inherit from here
/**
* Interface of reference counted object.
*/
class SKGE_API IReferenceCounted{
private:
uint m_RefCounter; /**< Number of references. */
protected:
/**
* Add Reference.
*/
inline void addRef() { m_RefCounter++; }
/**
* Remove reference.
* @return New reference value after decrement.
*/
inline uint removeRef() { m_RefCounter--; return m_RefCounter; }
public:
/**
* Constructor.
*/
IReferenceCounted():m_RefCounter(1){}
};
Now I have Resource that implement the reference counter:
/**
* Resource Handle.
*/
typedef ulong ResourceHandle;
/**
* Abstract Resource Class.
*/
class SKGE_API Resource: public IReferenceCounted{
friend ResourceManager;
protected:
String m_Name; /**< Resource name. */
ResourceHandle m_Handle; /**< Resource Handle. */
bool m_Created; /**< Indicates if the resource was created. */
ResourceManager* m_Creator; /**< Manager that created current resource. */
public:
/**
* Constructor. Sets resource information, but does not load it.
* @param _name Resource name.
* @param _handle Resource handle.
* @param _creator Manager that created current resource.
*/
Resource(const String& _name, ResourceHandle _handle, ResourceManager* _creator):
m_Name(_name),
m_Creator(_creator),
m_Handle(_handle),
m_Created(false)
{}
/**
* Destructor. Does not destroy the resource.
*/
virtual ~Resource(){}
/**
* Was the resource created?
* @return True if resource was created, otherwise false.
*/
inline bool created() const { return m_Created; }
/**
* Get resource name.
* @return Resource name.
*/
inline const String& getName() const { return m_Name; }
/**
* Get resource handle.
* @return Resource Handle.
*/
inline ResourceHandle getHandle() const { return m_Handle; }
};
Note: Resource manager is a friend of Resource to be able to do reference counting on it (if you noticed all IReferenceCounted's methods are protected), I don't like friend classes/function but making addRef()/removeRef() public IMHO brakes the encapsulation so I had to choose between two bad methods.
And the last one is ResourceManager
/**
* Abstract resource manager.
*/
class SKGE_API ResourceManager{
private:
static ResourceHandle sNextResourceHandle; /**< Next available resource handle. */
protected:
/**
* Resource maps.
*/
//@{
typedef stdext::hash_map<String, Resource*> ResourceNameMap;
typedef stdext::hash_map<ResourceHandle, Resource*> ResourceHandleMap;
//@}
ResourceNameMap m_NameResourceMap; /**< Name->Resource Map. */
ResourceHandleMap m_HandleResourceMap; /**< Handle->Resource Map. */
String m_ResourcesDirectory; /**< Directory where resources sits. */
/**
* Resource removal.
* Totally destroy and remove resource from the system.
* @param _resource Resource to remove and destroy.
*/
void _removeResource(Resource* _resource);
public:
/**
* Constructor.
*/
ResourceManager(){}
/**
* Destructor. Delete all resources
*/
virtual ~ResourceManager();
/**
* Get resources directory.
* @return Directory where resources sits.
*/
inline const String& getResourcesDirectory() const { return m_ResourcesDirectory; }
/**
* Set Resources Directory.
* @param _dir Resources Directory.
*/
inline void setResourcesDirectory(const String& _dir) { m_ResourcesDirectory = _dir; }
/**
* Get resource by name or handle.
* @return Resource or 0 if does not exists.
*/
//@{
Resource* acquire(const String& _name) const;
Resource* acquire(ResourceHandle _handle) const;
//@}
/**
* Release resource and if needed delete it from system.
* @param _resoruce Resource to release.
*/
void release(Resource* _resource);
};
Most of it self-explanatory, just to note acquire will search in the appropriate hash map and if found it will increase the ref calling addRef() and will return in, release on the other side will decrement the ref by calling removeRef() and if it reaches 0, it will call _removeResource() to totally delete and remove the resource from both hash maps.
Till this point everything is fine.
But most of you will notice that there is some obligations that if wont be followed will cause memory leaking.
Assume the following:
//Till this point resource was loaded
{
Resource* res = ResourceManager->acquire("Res1");
//Do some stuff with resource
//And here we must call this
ResourceManager->release(res);
}
Ugly ain't it? Too complicated, as user of this system I don't care about the way of freeing resources, especially via resource manager (there will be more that 1, for textures, meshes, fonts, particles, etc).
I could simply this by adding the following to Resource class:
void release(){
m_Creator->release(this);
}
(Assuming the resource was loaded by the manager, in other cases I would say RTFM :] )
Now the following code could be replaced to this one:
//Till this point resource was loaded
{
Resource* res = ResourceManager->acquire("Res1");
//Do some stuff with resource
//And here we must call this
res->release();
}
Nicer right?
But still not good, I still have to remember to free the resource.
So I thought about Smart Pointers, something like:
//In resource manager change acquire to this:
MageSharedPtr<Resource*> acquire(...);
//and the write like this
//Till this point resource was loaded
{
MegaSharedPtr<Resource*> res = ResourceManager->acquire("Res1");
//Do some stuff with resource
//And we don't need to care about freeing it, cause the destructor
//of MegaSharedPtr will call release() of the Resource*
}
And this is nice!
But it brakes when I have to expand my resource to a more generalized resources like Texture/Mesh.
Ill have to write conversions from MegaSharedPtr<Resource*> to MegaSharedPtr<Texture*> to be able to do it normally, cause Load() of Texture class would like to return Texture*, and acquire returns MegaSharedPtr<Resource*> that Ill have to cast to MegaSharedPtr<Texture*> or MegaSharedPtr<Mesh*> or etc..
So I'm kinda confused here and don't know how to do it right, it looks too complicated and I don't know how to simplify that :(
I would like to have some suggestions or articles about the topic.
Thanks a lot!