A generic handle-based resource manager

Greetings,

I've been working on a resource manager which intends (amongst other goals) to replace seg-fault prone pointers with handles. It's quite simple, but I've designed it to be flexible and safe since it will be one the foundations of my future games.

I'm sorry to bother you about this, but since I'm still a novice programmer and you guys are experts, could you please take a look at the code and tell me what you think could be improved so that the system is as stable, as error-free and as elegant as possible?

I've posted the code on a neat and simple online service entitled Codeviewer. This way post won't "overcrowded" with code. Here's the link: http://codeviewer.org/view/code:da6

I've divided the system in three essential components, all template classes:

CHandle
CHandleManager
CHandleMapper

CHandle is a very simple class which essentially acts as a read-only integer. That integer corresponds to the index in a std::vector on which the object at which the handle belongs too is stored. This index can only be altered by the CHandleManager which stores the same type of data that the handle points to.

CHandleManager works like a database which can only be accessed via valid handles, it is build upon a std::vector to store the actual Data and an std::stack to store free indexes formed when any element is removed. Notice that the HandleManager does not retain ownership over the data it stores, that data is stored in the form of pointers.

Last, but not least...well, maybe least, I have the CHandleMapper class whose sole purpose is to (surprise!) map a string to a valid handle (Node: The code link I posted doesn't ensure the handle is valid, but I've already corrected that).

Thank you in advance and Best Regards,
~Deimos
Couldn't you just do something like:

std::map<std::string, some_data_type> or boost::ptr_map<std::string, some_data_type>?
Now what fun would that be?
As an academic pursuit, reinventing the wheel can serve a purpose. As a means to an end though, it's often more of a hinderance than anything else.

IF your goal is to develop a flexible and safe resource manager to serve as the foundation for your future games, then reinventing the wheel will just divert you from that goal. Use existing containers to their full capabilities and you'll attain your true goals faster and more efficiently. Only create your own containers when the existing containers are not up to the task for some clear reason.
You assume that a handle with value zero is invalid. that isn't the case.

Why does the Manager know how to dereference a handle? Surely only a handle can do that.

I guess without seeing how you expect it to be used, it's difficult to critise, but it doesn't seem concise.
Thank you for your replies.

@jRaskell
Indeed, this could cause a delay in developing the actual game, but since I'm not programming on a deadline, but rather as a preparation for "official" learning and as a hobby, I guess that pretty much everything I code (or try to code) can be considered an academic pursuit. :)
And by the way, I assume you deduced from my previous answer that a single map would do the trick, but it doesn't, there are some features I want to have which a simple map can't handle. Not to mention my system actually uses both an std::map and an std::vector internally.

@kbw
You assume that a handle with value zero is invalid. that isn't the case.


Actually, my first model of the CHandle class featured (along with the index) a private bool member mValid, which could only be set and reset by the manager class, to ensure the handle was valid. But I figured that that extra member wasn't necessary if I established that an index of zero correspondes an invalid handle i.e. a null handle, and that's what I did.
Take a look at the following code from my manager class (line 71 of the code I originally posted):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
CHandle<Data> Handle;

if( mFreeIndexes.empty() )
{
    mInstances.push_back( &Instance );

    Handle.Assign( mInstances.size() );
}
else
{
    mInstances[ mFreeIndexes.top() ] = &Instance;

    Handle.Assign( ( mFreeIndexes.top()+1 ) );

    mFreeIndexes.pop();

}


As you can see, the value I'm assigning to Handle is the index of the stored data +1 thereby "reserving" zero for the null handle.


Why does the Manager know how to dereference a handle? Surely only a handle can do that.


I'm not sure I fully understand what you mean, maybe the definition of dereference which I'm using differs from yours. The manager is the one who dereferences the handle because it is the one who can actually access the data. However, I've designed this system so that new types, say a CImageHandle, can contact their manager, say Image Handle Manager (a singleton) and directly access the data they point at.

And you are right, I should have posted some sort of example usage, and for that I apologize.
Here's the piece of code I've been using to debug the system. It uses a dummy resource class, CImage, to exemplify what I expect to get out of this system.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
#include <iostream>

#include <string>

#include "CHandleManager.hpp"

#include "CHandleMapper.hpp"

#include "Singleton.hpp"

class CImage
{
    public:
        CImage();
        ~CImage();

        bool Load( const std::string & Name );
        void Unload();

        const std::string & GetName() { return mName; }

    private:
        std::string mName;
};

CImage::CImage()
{

}

CImage::~CImage()
{

}

bool CImage::Load( const std::string & Name )
{
    mName = Name;

    return true;
}

void CImage::Unload()
{

}

using namespace std;

typedef CHandleManager<CImage> ImageHandleManager;
typedef CHandleMapper<CImage> ImageHandleMapper;

ImageHandleManager & ImgHndMgr = Singleton<ImageHandleManager>::GetInstance();
ImageHandleMapper & ImgHndMpr = Singleton<ImageHandleMapper>::GetInstance();

class CImageHandle
{
    public:
        CImageHandle(const CHandle<CImage> & Handle) : mHandle( Handle ) { }

        CImageHandle & operator=(const CHandle<CImage> & Handle) { mHandle = Handle; return ( *this ); }

        CImage * operator*() { return ImgHndMgr.Dereference( mHandle ); }
        CImage * operator->() { return ImgHndMgr.Dereference( mHandle ); }

        operator CHandle<CImage>() { return mHandle; }
        operator CHandle<CImage>&() { return mHandle; }

    private:
        CHandle<CImage> mHandle;


};

int main()
{
    CImage AwesomeImage, CoolImage, DecentImage, BadImage, TerribleImage;

    AwesomeImage.Load( "./gfx/Awesome.png" );
    CoolImage.Load( "./gfx/Cool.png" );
    DecentImage.Load( "./gfx/Decent.png" );
    BadImage.Load( "./gfx/Bad.png" );
    TerribleImage.Load( "./gfx/Terrible.png" );

    CImageHandle ImgHandle1 = ImgHndMgr.Insert( AwesomeImage );
    CImageHandle ImgHandle2 = ImgHndMgr.Insert( CoolImage );
    CImageHandle ImgHandle3 = ImgHndMgr.Insert( DecentImage );
    CImageHandle ImgHandle4 = ImgHndMgr.Insert( BadImage );
    CImageHandle ImgHandle5 = ImgHndMgr.Insert( TerribleImage );

    cout << ImgHandle1->GetName() << endl;

    ImgHndMpr.Insert( "Img_Awesome", ImgHandle1 );
    ImgHndMpr.Insert( "Img_Cool", ImgHandle2 );
    ImgHndMpr.Insert( "Img_Decent", ImgHandle3 );
    ImgHndMpr.Insert( "Img_Bad", ImgHandle4 );
    ImgHndMpr.Insert( "Img_Terrible", ImgHandle5 );

    CImageHandle Temp = ImgHndMpr.Get( "Img_Terrible" );

    cout << Temp->GetName() << endl;

    ImgHndMgr.Remove( Temp );

    CImage AwfulImage;
    AwfulImage.Load( "./gfx/Awfull.png" );

    ImgHandle5 = ImgHndMgr.Insert( AwfulImage );

    cout << ImgHandle5->GetName() << endl;

    return 0;
}


The idea is to have an actual ImageManager (instead of a typedef) which would have a HandleManager for Image Handles, but I think you get how I plan to use it from this. Ownership, Loading/Unloading would be some of the responsibilities of this ImageManager class.

Again, if you spot any bug or design flaw, please do post back!

Sorry about the long post!

Best Regards,
~Deimos
Last edited on
You have to make up a CImageHandle rather than just use CHandle<CImage>? That's even more work. These sorts of things are meant to make use easier. If you have to do extra work to use it, you're headed in the wrong direction.

I didn't think an object like a CImage needed a handle. You already have an object and pass it around by reference as cheaply as a handle. I imagined your handle class being used to hold handles, integral type that are returned from open and are closed.

I can't quite see the value in what you've done.
Topic archived. No new replies allowed.