Resource Management System -- Part 2
In Part 1 of this series, we looked at the basic structure of our resource management system. Now, we're going to implement the core class of our system, the Handle. You might want to go back and review the responsibilities behaviors of the handle class, which I discussed in part 1. Remember, the real power of this design is contained within the handle class. It's the object that is moved around between subsystems inside the engine, and it's the only thing clients should see. Let's jump right into the code for it:
template<typename Type> class ManagerBase; template<typename Type> class Manager; //defines a resource handle to work with template<typename Type> class Handle { private: friend ManagerBase<Type>; friend Manager<Type>; //internal id is just an integer (0 is reserved for invalid ids) typedef std::size_t resource_id; resource_id m_Id; Manager<Type>* m_Parent; //construct a valid handle Handle( resource_id Id, Manager<Type>* Parent ) : m_Id( Id ), m_Parent( Parent ) { assert( m_Id != 0 ); assert( m_Parent != NULL ); m_Parent->AddRef( *this ); } public: //construct an invalid handle Handle() { m_Id = 0; m_Parent = NULL; } ~Handle() { if( m_Parent != NULL ) m_Parent->Release( *this ); } //copy a handle (works on valid and invalid handles) Handle( const Handle& Other ) : m_Id( Other.m_Id ), m_Parent( Other.m_Parent ) { if( m_Parent != NULL ) { m_Parent->AddRef( *this ); } } //assign a handle (works on valid and invalid handles) const Handle& operator = ( const Handle& rhs ) { if( m_Id && m_Parent ) m_Parent->Release( *this ); m_Id = rhs.m_Id; m_Parent = rhs.m_Parent; if( m_Parent != NULL ) m_Parent->AddRef( *this ); return *this; } bool operator < ( const Handle<Type>& rhs ) const { return m_Id < rhs.m_Id; } bool operator > ( const Handle<Type>& rhs ) const { return m_Id > rhs.m_Id; } bool operator == ( const Handle<Type>& rhs ) const { return m_Id == rhs.m_Id; } bool operator != ( const Handle<Type>& rhs ) const { return m_Id != rhs.m_Id; } bool Valid() const { return m_Id != 0; } };
You might notice that the comparison operators don't take the parent into account. I've assumed that two handles being compared always have the same parent. If this isn't the case in your code, you should make sure to alter the checks with '&& m_Parent == rhs.m_Parent'.
Now, behavior. First of all, you should notice that the only constructor to create a valid handle is private. Only a manager can create a real handle. Everyone else can create an invalid handle, copy an existing handle, or assign from an existing handle. Handles can be compared; the comparison uses their internal ids, without exposing the ids to the client. I haven't included a full set of comparison operators for brevity's sake. The main reason for equality and inequality operators is so that we can figure out whether or not two handles refer to the same object. The other operators, operator < in particular, are designed to help using handles in STL containers that rely on functors such as less<_T>. And that sums up all of the functionality of the handle. Clients can't do much of anything with it, or modify it in any way.
A word about managers. In the code above, there's a forward declare for a templated Manager and a templated ManagerBase. Both classes will be implemented in the next part. ManagerBase will carry functionality that is common to all managers. Manager will be specialized for each resource type, and will carry logic that is specific to that resource type. For the next part in this series, I'll be implementing a manager to work with images. It'll allow us to control when we load and unload images, as well as making sure that we never load an image more than once.
1 Comments:
Nice. Very interesting system. Any idea when the next chapter in the serise will be up?
By
Anonymous, at 10/28/2005 5:22 AM
Post a Comment
<< Home