Jump to content
  • Advertisement
Sign in to follow this  
Symphonic

A Simple Templated Resource Manager

This topic is 4695 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Download rmanager.h Hi everyone, this is a simple C++ templated resource manager, I'm looking for feedback and in particular, need help making the template prototype simpler, currently it looks like this:
template<
    class resource_type,
    class load_options_type,
    class loader_type,
    loader_type loader, 
    class cleaner_type,
    cleaner_type cleaner>
class rmanager{...};
Ok, so what is all that; -> resource_type is the type of resource that's going to be handled by the manager -> loader is an instance of loader_type, it has to implement resource * operator () (std::string location, options_type options) and can be either a function pointer, or an actual class -> cleaner is an instance of cleaner_type, it has to implement void operator()(resource *) and it is assumed that when it is called the resource pointer that was passed is cleaned up Why do I need help; It does work right now, as-is, but I would like not to have to pass loader_type and cleaner_type as arguments to the template, ideally, those would be implicit in the types of loader and cleaner. So how can I eliminate those? Any feature recommendations are welcome as well; Currently, the manager assumes uniqueness of resources based on location, each resource is only loaded once, and can be named any number of different names, bool rmanager::load(string name, string locator[, options_type options]) links name to locator, and loads the resource if it is not already loaded bool rmanager::free(string name) removes name from the names-list, and if that was the last name linked to the resource, deallocates the resource as well. const resource_type & rmanager::get(string) returns a const reference to the resource many thanks for any help that can be offered :D

Share this post


Link to post
Share on other sites
Advertisement
Personally, I would replace all the parameters excluding the resource_type with a "Policies" template parameter. The Policies class can be a template class if you like, where you have typedefs to load_options, loader, &c.

E.g.:
struct DefaultLoadOptions {};

struct DefaultLoader {
void load() {}
};

struct DefaultPolicies {
typedef DefaultLoadOptions load_options_type;
typedef DefaultLoader loader_type;
// Etc..
};

// Here's a template version:
template<
class LoadOptions = DefaultLoadOptions,
class Loader = DefaultLoader
> class Policies {
typedef LoadOptions load_options_type;
typedef Loader loader_type;
};

template<
class Type,
class Policies = DefaultPolicies // or class Polices = Policies<>
> class ResourceManager {
typedef typename Policies::loader_type loader_type; // Convenience typedef if you use loader_type a lot.
public:
void load() {
loader_type loader;
loader.load();
}
};





Just my opinion, though...


jfl.

[edit] Minor edits.

[Edited by - jflanglois on January 11, 2006 6:06:03 AM]

Share this post


Link to post
Share on other sites
A few random thoughts, in no particular order:

1. What is the use for int ids? It brings one additional level of indirection and IMO only complicates matter. You could be using pack* instead.

2. In default constructor, it should be
default_options_available = false

3. Using const std::string & as function arguments is pretty standard.

4. Consider allowing generator() to throw exceptions. A little reformat of load function and you're good to go.

5. How can you use generator and cleaner with functors? I can't see that with passing instance directly. If it's only for functions, why bother passing function type if it's always the same? You could restrict those parameters to functors only and then if you wanted to use a function, the user would have to hide it in some standard wrapper (from std).

Share this post


Link to post
Share on other sites
I see, maybe I'm implementing this ass-backwards, but in your example, you're instantiating a load object at load time, but if the loader is just a function pointer that won't work. Which is why, as I have made it, an instance of the loader is passed when the manager is created (the same goes for the cleaner).

What ends up happening is that the loader requires two arguments in the template because the first defines its type, and the second is the instance itself.

would it make more sense to do it this way;

class loader{
static res * load(string,opts);
}

class cleaner{
static void clean(res *);
}

// ...

resource_manager<res, opts, loader, cleaner>{
res * load(string s,opts o){
return loader::load(s, o);
}

void clean(res * r){
cleaner::clean(r);
}

// ...
};

A final note: in my mind it doesn't make sense to roll the opts, loader, and cleaner into a policies class, because loader and options must be intrinsically tied to res, ie there cannot exist a default loader and options object. The only thing that can be defaulted is cleaner (which would be just to delete the pointer) but even so, I wouldn't create a policy object to replace a single template argument, I would just change the arguments so they looked more like this:

template<
class resource_type,
class options_type,
class loader,
class cleaner=DeleteIt>
resource_manager{...};

Share this post


Link to post
Share on other sites
Quote:
Original post by deffer
A few random thoughts, in no particular order:

1. What is the use for int ids? It brings one additional level of indirection and IMO only complicates matter. You could be using pack* instead.

do you mean the named_resources and loaded_resources maps are std::map<std::string, pack*>?

EDIT: Done, updated.

Quote:
2. In default constructor, it should be
default_options_available = false

Fixed. Updated.

Quote:
3. Using const std::string & as function arguments is pretty standard.

I never bought into that, because std::string provides copy-on-change, and passing by reference actually adds a level of indirection that is not there if you just pass the strings around themselves.

Quote:
4. Consider allowing generator() to throw exceptions. A little reformat of load function and you're good to go.


EDIT: OOPS I see what you meant, fixed and updated ;)

Quote:
5. How can you use generator and cleaner with functors? I can't see that with passing instance directly. If it's only for functions, why bother passing function type if it's always the same? You could restrict those parameters to functors only and then if you wanted to use a function, the user would have to hide it in some standard wrapper (from std).
Well, this is the weakest part of the code right now anyway so it's open to change.

The simple quesiton is using it with functors, if you pass an instance of a class that implements a functor that matches the requirements of the load function, then it will be used.

The problem that I'm crunching on is how do you allow the user to pass an instance of a class, not the type of a class, to be a generator for resources. As I said, this solution works, but it's horribly inelegant.

Am I missing something?

[Edited by - Symphonic on January 11, 2006 10:26:38 AM]

Share this post


Link to post
Share on other sites
Quote:
Original post by Symphonic
Hi everyone, this is a simple C++ templated resource manager, I'm looking for feedback and in particular, need help making the template prototype simpler, currently it looks like this:


template<
class resource_type,
class load_options_type,
class loader_type,
loader_type loader,
class cleaner_type,
cleaner_type cleaner>
class rmanager{...};



I would suggest what you want is

template<
class resource_type,
class load_options_type,
class loader_type,
class cleaner_type>
class rmanager{
public:
rmanager(loader_type &loader, cleaner_type& cleaner);
// ...
private:
loader_type m_loader;
cleaner_type m_cleaner;
};


Have compile-time stuff set at compile time, run-time stuff set at run time.

Share this post


Link to post
Share on other sites
Quote:
Original post by Symphonic
A final note: in my mind it doesn't make sense to roll the opts, loader, and cleaner into a policies class, because loader and options must be intrinsically tied to res, ie there cannot exist a default loader and options object.

How come you allow the user to pass an arbitrary options type, while admitting that it's got to be strongly tied to loader and/or resource classes? IMO it should be defined as internal typedef in loader class.

I would also put cleaner class functionality into loader class and drop the former completely. Those are so strongly tied together, that I cannot imagine changing one with no impact on another.

Quote:
Original post by Symphonic
Am I missing something?

What Bregma said ;)

Share this post


Link to post
Share on other sites
Quote:
Original post by Symphonic
Quote:
Original post by deffer
3. Using const std::string & as function arguments is pretty standard.

I never bought into that, because std::string provides copy-on-change, and passing by reference actually adds a level of indirection that is not there if you just pass the strings around themselves.

The technical term is copy-on-write, and the C++ standard allows it as an optimisation, it does not require it. In fact recently copy-on-write has been falling out of favour as an optimisation since in a multithreaded environment the additional locking required makes it a pessimisation instead. Out of my five compiler versions only two have a default std::string implementation using copy-on-write and one of those is an older version of a compiler where the newer version does not use copy-on-write by default.

Enigma

Share this post


Link to post
Share on other sites
Quote:
Why do I need help;
It does work right now, as-is, but I would like not to have to pass loader_type and cleaner_type as arguments to the template, ideally, those would be implicit in the types of loader and cleaner. So how can I eliminate those?


When I made a generic templated manager, I solved that problem though a function pointer - if the user did not specify/need a 'cleaner', I did not force that upon them and if they needed it, they then called SetProcessFunctionPtr(ProcessFuncPtr. Take a look at my code and you can see what I mean, I'd suggest you take a similar approach - I've found it to be the best so far in terms of flexibility and usability. Good luck with your manager!

Share this post


Link to post
Share on other sites
Quote:
Original post by Bregma
I would suggest what you want is

template<
class resource_type,
class load_options_type,
class loader_type,
class cleaner_type>
class rmanager{
public:
rmanager(loader_type &loader, cleaner_type& cleaner);
// ...
private:
loader_type m_loader;
cleaner_type m_cleaner;
};


Have compile-time stuff set at compile time, run-time stuff set at run time.


Bregma, spot-on, I guess I was thinking with my spleen when I designed that.

Fixed & Updated. The new version behaves like this:

// res, opt, gen and clean are all class names
gen g; clean c;
resource_manager<res, opt, gen&, clean&> rmgr(g,c);


Quote:
Original post by deffer
How come you allow the user to pass an arbitrary options type, while admitting that it's got to be strongly tied to loader and/or resource classes? IMO it should be defined as internal typedef in loader class.

I would also put cleaner class functionality into loader class and drop the former completely. Those are so strongly tied together, that I cannot imagine changing one with no impact on another.

That is a sensible way forward, I'm thinking about something like a caretaker class that defines the resource and locators as well, and disappearing the options mechanism to make it part of the locators.

That way, say if the user wants to, the same texture could be loaded with different filtering under different names, if the locator class differentiates between them.

Perhaps omething that looks more like this:

class exampleCaretaker{
public:
struct resource{...}
struct locator{} // must define operator < () for maps to work

resource * load(const locator & loc){...}
void free(resource * res){...}
}

template<class caretaker>
class resource_manager{
private:
//...
caretaker ct;
load(caretaker::locator loc){
return ct.load(loc);
}
release(resource * r){
ct.free(r);
}

public:
//...
resource_manager(caretaker c):ct(c){}
};

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!