Some questions on programming a resource pool

Started by
6 comments, last by Guthur 15 years, 4 months ago
Suppose I want to write a simple resource pool. Its only purpose is to make sure that resources are not created more than once. This type of thing seems to be discussed a lot around here, but I'm still unclear on a few things: 1. Usually a system like this uses reference counting (usually via smart pointers) to make sure that a resource is automatically released when it is no longer used. Is this really desirable? For example, say you have an explosion effect that uses some textures. Such an effect can be created and destroyed many times during a game, but you probably don't want the associated textures to be reloaded each time. I guess you can load them "externally" (maybe using an effect factory or something) and pass a reference to the effect. Do you think this is a good solution, or would it be better if the resource pool took care of it? If it's the latter, one option I can think of is when you create a resource, you also specify if it should be destroyed when its reference count becomes zero. If not, it will be released when the resource pool goes out of scope. 2. I want the resource pool to work with any resource type, and different resources can be created in different ways (i.e., not necessarily with operator new). I see two ways to accomplish this: a) When creating a resource pool for a specific type (i.e., specifying the type as a template parameter), you pass it a custom creation functor. The problem is that each resource can require different creation parameters and so I'm not sure about what signature the functor should have. It could take a file name and a void pointer which it will interpret accordingly, but this isn't very type-safe. b) Put a pure virtual createResource() function in the resource pool class and derive different resource pools, each one providing a custom implementation for its resource type. Of course the pooling logic will be in the base class. In this case I will also need to pass a void pointer as a second parameter, but the advantage here is that I can make createResource() protected and the derived resource pools could provide a type-safe wrapper around it. For example:

template <class T>
class ResourcePool {
private:
    virtual boost::shared_ptr<T> createResource(const std::string &fileName, void *data) = 0;
};

class SurfacePool : public ResourcePool<SDL_Surface> {
private:
    boost::shared_ptr<SDL_Surface> createResource(const std::string &fileName, void *data) {
        ...
    }

public:
    boost::shared_ptr<SDL_Surface> createResource(const std::string &fileName, const Color &colorKey) {
        return createResource(fileName, &colorKey);
    }
};
I just typed this in quickly so there might be somethings I haven't thought about, but I do remember doing something like this a while ago and it seemed to work pretty well. However, I've heard that there are some disadvantages to this approach. I think it had something to do with DLLs/plugins. That's all I got so far. Kind of a long post for just two questions. :) Any thoughts, comments or suggestions are appreciated.
Advertisement
It seems like if you go with the method you've written up, you're still basically writing a different pool for each resource type. ie, you haven't actually made it generic. You could also accomplish the same thing with template specializations and function overloading (unless there's a reason you need a generic base class, ie containers).

You might investigate using Boost::bind to create uniform functors that can be stored in a map.

ie
template<class Key, class Val>class ResourcePool{public:    //....private:    //here's your map    std::map<Key, boost::function<boost::shared_ptr<Val>()> factories;};//...ResourcePool<std::string, SDL_surface> pool;//register a resourcepool.registerResource("some_surface", boost::bind(&createSDLSurface, some_filename, some_colorkey));//create the resouceboost::shared_ptr<SDL_Surface> my_surface = pool.create("some_surface");


Haven't tested this, just shootin from the hip.

[Edited by - mb108 on December 2, 2008 4:48:50 PM]
If you want the parameters could you not just create a LoadParams base object and then use runtime polymorphism within the functor to get the info you need.

As for storage I'm using a plain old static std::vector<T> and then Object IDs for lookup. Theres probably loads of limitations but this is my first exploration of generics, i think i could add reference counting easy enough though.
Innovation not reiterationIf at any point I look as if I know what I'm doing don't worry it was probably an accident.
Quote:Original post by mb108
It seems like if you go with the method you've written up, you're still basically writing a different pool for each resource type. ie, you haven't actually made it generic.


The derived classes just implement the loading functionality and provide type-safe loading functions. The pooling functionality is still in the base class.

I'm not very familiar with boost's function and bind so I'm not sure how your code works, but my intention was to be able to supply a different parameter for each call:

boost::shared_ptr<SDL_Surface> s1 = pool.create("ship", Color(128, 64, 0));boost::shared_ptr<SDL_Surface> s2 = pool.create("rock", Color(255, 0, 255));


Does your code allow this?

Quote:Original post by Guthur
If you want the parameters could you not just create a LoadParams base object and then use runtime polymorphism within the functor to get the info you need.


I'm not sure what you mean. Could you please elaborate a bit?
Sorry my usage of terminalogy may have been incorrect :| Anyway something like below and then use RTTI to downcast, not brillant but should work. If I have made a wrong assumption please someone mention it, i'm sure y'all will :p.

class ParamBase{public:	ParamBase() {}	virtual ~ParamBase() = 0;};class TextureParams : public ParamBase{public:        TextureParams() {}        TextureParams(int width, int height) {.....}        ~TextureParams() {}	int Width, Height;};boost::shared_ptr<SDL_Surface> createResource(const std::string &fileName, ParamBase *params)
Innovation not reiterationIf at any point I look as if I know what I'm doing don't worry it was probably an accident.
Ya i know i actually did mean to mention that, but it does seem more intuitive than void pointer and isn't much extra work. I actually need to think more about this one myself anyway because at the minute i just pass a file name, and actually just as i write that it gives me and idea; maybe external resources ie from file should come with header information :)
Innovation not reiterationIf at any point I look as if I know what I'm doing don't worry it was probably an accident.
Quote:Original post by Gage64
I'm not very familiar with boost's function and bind so I'm not sure how your code works, but my intention was to be able to supply a different parameter for each call:

boost::shared_ptr<SDL_Surface> s1 = pool.create("ship", Color(128, 64, 0));boost::shared_ptr<SDL_Surface> s2 = pool.create("rock", Color(255, 0, 255));


Does your code allow this?


Eh, in a manner. The idea with bind is that you create function object out of the function AND the arguments to pass to it, so the resulting functor no longer requires any arguments. Every combination of arguments that you are going to use would have to be registered before creating. "blueship", "redship", "greenship" would each have functors associated with them.

This is a good thing or a bad thing depending on how you look at it.
I wanted to create an accessor object for the resource list and think i may have stumbled upon a nice solution for loading res as well :). A parameterized friend class of the resource holder and then specialisation of that 'Accessor' for each resource type containing an appropriate static load function.

A bit like:

template <typename T>class ResHolder{    template<typename T> friend class Accessor;......}template<typename T>class Accessor{public:    static T* LoadRes(Param param) {}}template<>class Accessor <Texture>{public:    static Texture* LoadRes(Param param) {....}}

Innovation not reiterationIf at any point I look as if I know what I'm doing don't worry it was probably an accident.

This topic is closed to new replies.

Advertisement