Generic Resource Management

I am writing some auxiliary classes for the game engine I want to create with SFML. I want to write generic resource management code so I can then use mostly the same code to manage loading of images, sounds etc. I have a few questions about how to write it. Sorry if this post is a bit long or poorly worded!

This is my Resource abstract base class. The Load functions are for loading resources (from file, memory or other) and similarly for saving them. The saving functions are not pure virtual since not all resources should be able to be saved. Freeing of resources is handled by overriding the pure virtual destructor.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Resource {

	public:
		Resource() {  }
		virtual ~Resource() {  }

		virtual void Load(std::string arg)=0;
		virtual void Load(char* arg)=0; // I have used char* for memory pointer since
                                                // I need void* for generic arguments
		virtual void Load(void* arg)=0;

		virtual void Save(std::string arg) {}
		virtual void Save(char* arg) {}
		virtual void Save(void* arg) {}
};


I want to support loading from generic arguments so the user can choose write custom loading functions not only given an std::string argument or a memory pointer argument but also any other argument type they might want. At the moment I have implemented this using a void* type. However, this means that if the user of the library wants to write two custom loading functions with different argument types, it will get very cumbersome (the void* would have to contain data to tell the function which loading method to use etc).

My question, therefore, is whether there is a better way to allow the user to implement loading and saving functions given an arbitrary argument type?



My other questions regard the resource management class. Here is the code for it so far.

Possibility 1
1
2
3
4
5
6
7
8
9
10
11
12

template <class T> class ResourceManager {

		std::map<std::string, T*> resources;	

	public:
		std::string Load(void* arg);
		void Save(void* arg);

		T* GetResource(std::string ref);
};


My first question here is: is it too inefficient for a game resource manager to refer to resources using an std::string and std::map? (The reason I have done it is that it will make it easy to see if a file has already been loaded by giving the loaded resource it's relative file path as an identifier).

The second, more important question is this. Should I make it a template class so the user can use a different instance of the template for each Resource type? In this case, is there any way I could force the user to only use types derived from Resource (like interfaces in C#).

Alternately, should I just use

std::map<std::string, Resource*>

and make the user provide custom loading and saving functions (which I have placed as template arguments above)?

In this case the code would be something like:

1
2
3
4
5
6
7
8
9
10
11
12

template <void (*Loader)(void*), void (*Saver)(void*)=0> class ResourceManager {

		std::map<std::string, Resource*> resources;	

	public:
		std::string Load(void* arg);
		void Save(void* arg);

		Resource* GetResource(std::string ref);
};


Thanks in advance for any help.
Last edited on
What does the Resource class really buy you?

You could create a ResourceLoader class and a ResourceSaver class.

If you are intent in the Resource class, I recommend splitting the interface into LoadableResource and SaveableResource interfaces.

Void pointers are a sure sign of trouble. If you really have to go this route, consider using boost::any or boost::variant instead. Why can't you just require resources to be loaded from a URI of some sort specified with a string?
OK. I've made some changes to the Resource Manager code. I removed the void* pointers and have just gone with the std::string URI route. I will also put saving of data in another class.

The reason I had the Resource class is so a single instance of ResourceManager could contain multiple types of resources, but this is probably a bad idea anyway.

The only remaining problem is the fact that in my code I assume that class T has a void Load(std::string&) member function. Clearly this is not type safe and I must do something about it. The only two things I can think of for this problem. The first is dynamic_cast. This would mean that I do need the abstract base class resource, but I pretty sure this code will be frowned upon as it's very clunky.

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
template<class T> class ResourceManager {
	std::map<std::string, T*> resources;
public:
        ResourceManager()
        {
                // check using dynamic cast
                T& temp;
                try { dynamic_cast<Resource&>(temp); }
                catch (std::bad_cast)
                {
                       // class T was not derived from resource so deal with error
                       throw new BadResourceClass(); // or something like this
                }
        }
	~ResourceManager()
	{
		// iterate through std::map and free resources
		std::map<std::string, T*>::iterator itr;
		for (itr = resources.begin(); itr != resources.end(); itr++)
			delete itr->second;
		resources.clear();
	}

	std::string& Load(std::string& uri)
	{
		if (resources.find(uri) == resources.end()) // only load if URI is not already in list
		{
			T* temp;
			try {
				temp = new T;
			}
			catch (std::bad_alloc)
			{
				// handle error	
			}
			Loader(uri, *temp);	// load function which user must have defined in their class T

			resources.insert(std::pair<std::string, T*>(uri, temp));
		}
		return uri;
	}

	/// Get a pointer to an actual resource instance given it's unique std::string ID.
	T* GetResource(std::string& ref)
	{
		std::map<std::string, T*>::iterator itr = resources.find(ref);
		if (itr == resources.end())
			return 0;
		return itr->second;
	}
};


The only other option I can think of is to put the loader function as another template argument (as I did in my previous post) rather than a member function of class T.
I.e. template <class T, void (*Loader)(std::string&)> ResourceManager { ... };

In this case the code would be:

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
template<class T, void (*Loader)(std::string&, T&)> class ResourceManager {
	std::map<std::string, T*> resources;
public:
        ResourceManager() {}
	~ResourceManager()
	{
		// iterate through std::map and free resources
		std::map<std::string, T*>::iterator itr;
		for (itr = resources.begin(); itr != resources.end(); itr++)
			delete itr->second;
		resources.clear();
	}

	std::string& Load(std::string& uri)
	{
		if (resources.find(uri) == resources.end()) // only load if URI is not already in list
		{
			T* temp;
			try {
				temp = new T;
			}
			catch (std::bad_alloc)
			{
				// handle error	
			}
			Loader(uri, *temp);	// load function which user must have defined in their class T

			resources.insert(std::pair<std::string, T*>(uri, temp));
		}
		return uri;
	}

	/// Get a pointer to an actual resource instance given it's unique std::string ID.
	T* GetResource(std::string& ref)
	{
		std::map<std::string, T*>::iterator itr = resources.find(ref);
		if (itr == resources.end())
			return 0;
		return itr->second;
	}
};


Sorry for making you read through all this code, but if you have any suggestions as to which method to use, improvements on a method or indeed an entirely new one then any input would be greatly appreciated.
Last edited on
Topic archived. No new replies allowed.