Sign in to follow this  

iterating through similar classes with std::list

This topic is 4855 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

Alright. Here is the problem. I have a resource class, called CResource.
template <class resource> class CResource {
   private:
      std::string name;
      resource *data;
   ...
}
This allows me to register anything that is a class as a resource to my Resource Manager. My resource manager is simply three lists of CResources (std::list<CResource *>): the alive resources, the load que, and the dead resources. Now here is my problem: I have to be able to loop through these resource lists no matter what type of class the template is to be able to find certain resources by name. For example, if I want to load a texture class, I want to be able to loop through all three lists. However, within these lists there may be resources for mesh files, etc. So when I try to set my temporary CResource to the iterator so that I can check values, return it, etc...well, it wont work because they have different values. Does anyone have any ideas as to how this can be solved? I really want to keep my resource manager as "vague" as possible, so that anything that is a class can be considered a resource, and I am not stuck with lists of textures, meshes, etc. The only idea I have is that instead of using the template to create a set of data, instead use it to inherit from a class. So instead of the above code, I would use
template <class resource> class CResource : resource {
Would that work when I try to set my temporary CResource of one type to a CResource of a different type? Thanks for any help

Share this post


Link to post
Share on other sites
You derive all classes you want to be able to be stored from one base class and use std::list <BaseClassOfAllClasses *>. Then maybe with some RTTI cast them to their original type.

This solution may not be what you want and if you use RTTI it will also be slow. Slow especially if you use it as I think, you want to use it - list of sprites and others... Imagine dynamic_cast every frame for every sprite...

In my engine, I have a template class, which stores only objects of one type. So then I may use just Managed <Sprite> sprites; Managed <Whatever> whatevers;. I think, you should go this way. It's one class in fact. One templated one. You just have to create few instances to store whatever you want... I'm afraid that's nearest match of your idea [smile]

Oxyd

Share this post


Link to post
Share on other sites
I'd personally recommend going the inheritance route. Many of the functions wouldn't even need to be virtual, such as retrieving the name of the object. And even for virtual function calls, it's still just a simple vtable lookup. I [personally] think it would make your design much cleaner to use inheritence, and simply have a list for all types of resources, like you're wanting, rather than have a different list for each type. And I doubt that performance will be an issue. A vtable lookup is so minor compared to what is usually done after that lookup.

Share this post


Link to post
Share on other sites
Quote:
Original post by visage
Why dont you just say
Whatever whatevers
, instead of
Managed<Whatever> whatevers
? What is the purpose of the managed class?


Because, I'm stupid [smile]

The purpose of Managed class is managing my memory. It contains linked list of objects and when you ask it for some more (or less) memory (object count) it allocates the memory when it has time. It cooperates with TaskManager class, which assigns it some timespan, if the engine is running at, say, 400 FPS and you really only want 100 FPS (no point for running such fast) it gives some time to every instance of class Managed to preallocate some memory (or free some, if it has too much objects).

Basically, the only functionality I wanted to show you is it contains list of objects.

Maybe I should've said std::list <Whatever *> whatevers; as it would be less confusing. (Looks like, I forgot, you weren't familliar with my engine [smile])

I'm sorry for the confusion [smile]

@Agony: But Sprite class obviously does something totally different than, say, Texture class. And you store only Object *'s. Now - you want to get your hero texture. How do you dig it from the list? You have to take every object, and see wheter it is Texture type, if yes, dynamic_cast it and see, wheter it's the texture you want.

If you had two std::lists (std::list <Sprite *> sprites; std::list <Texture *> textures;) you just iterate thought the textures list and look at what it contains.

Of course having list of textures and searching for some concrete texture is probably stupid example, but I couldn't think of anything better [smile]

Oxyd

Share this post


Link to post
Share on other sites
If you need to search for resources by name, though, and if all resources have a name, then you shouldn't care about checking each one to see if it is a texture or a mesh or whatnot. Just check the name. If the name is the same, then just for safety's sake you might check to make sure it is of the type you're wanting. But you don't need to check every single item's type. Since the resource name's accessor shouldn't be virtual anyway, I don't see how inheritance will cause any slowdown. I could be missing something, though...

Share this post


Link to post
Share on other sites
If I were you I would create a base class containing the functions that all resources have in common:

class IResource {
public:
virtual void getName()=0;
...
};

And then create a templated class like this:

template <class resourceType> class CResource {
private:
std::string name;
resourceType *data;

public:
void getName() {
return name;
}
...
};




Quote:

You derive all classes you want to be able to be stored from one base class and use std::list <BaseClassOfAllClasses *>. Then maybe with some RTTI cast them to their original type.

Why would he want to cast them back to there original type when it makes much more sense to use virtual functions and a base class.

Share this post


Link to post
Share on other sites
Quote:

Quote:

You derive all classes you want to be able to be stored from one base class and use std::list <BaseClassOfAllClasses *>. Then maybe with some RTTI cast them to their original type.

Why would he want to cast them back to there original type when it makes much more sense to use virtual functions and a base class.


So you want to say, the base class would contain all interfaces of all classes? Say - you have Mesh class wich has load method. Then you have Sprite class which has no load method, but has render method. Thus the base class would have to have both load and render methods, if you want to prevent casting to original type.

Imagine you have 20 totally different classes and one super class, which contains all the interfaces...

Oxyd

Share this post


Link to post
Share on other sites
Quote:

So you want to say, the base class would contain all interfaces of all classes? Say - you have Mesh class wich has load method. Then you have Sprite class which has no load method, but has render method. Thus the base class would have to have both load and render methods, if you want to prevent casting to original type.

A sprite isn't the same type of object as a mesh, so therefore they don't belong together. You should group the objects into hierarchies that make sense. In this example, the mesh object shouldn't be stored in the same container as the sprite.

*sigh*

I'm not very good at explaining things, so I really don't know how to explain it any more. Just design it how you think you should; you'll eventually get what I'm talking about :/

Share this post


Link to post
Share on other sites
I think, you just said you mean the same as me [smile]

Have a std::list for sprites, then std::list for meshes, right?

This is what I meant [smile]. Obviously none of us is good at explaining things [smile]

Oxyd

Share this post


Link to post
Share on other sites
Quote:
Original post by visage
Alright. Here is the problem. I have a resource class, called CResource.


template <class resource> class CResource {
private:
std::string name;
resource *data;
...
}


This allows me to register anything that is a class as a resource to my Resource Manager. My resource manager is simply three lists of CResources (std::list<CResource *>): the alive resources, the load que, and the dead resources.


I assume that means your resource manager is also parameterized by resource aswell? because this:


std::list<CResource *>


wont compile without a type given to the type parameter of CResource but i'll assume that your resource manager is also a class template that passes its parameter to "CResource".

Quote:
Original post by visage
Now here is my problem: I have to be able to loop through these resource lists no matter what type of class the template is to be able to find certain resources by name. For example, if I want to load a texture class, I want to be able to loop through all three lists. However, within these lists there may be resources for mesh files, etc. So when I try to set my temporary CResource to the iterator so that I can check values, return it, etc...well, it wont work because they have different values.


if i understood correctly i don't see what the problem is unless you've forgetten to use typename when you tried to use list's iterator for generic code, simple silly example:


#include <deque>
#include <algorithm>
#include <functional>
#include <string>
#include <iostream>

template < typename resource >
class Resource {

const std::string _name;
resource data;

public:

typedef Resource<resource> _Self;

Resource(const std::string& res_name = std::string("NULL RESOURCE"),
const resource& r = resource())
: _name(res_name), data(r) {}

const resource& res() const { return data; }

_Self& res(const resource& r) {
data = r;
return *this;
}

const std::string& name() const { return _name; }

};


template < typename res_type >
class ResourceMgr {
public:

typedef Resource<res_type> resource_type;
typedef const resource_type* const const_res_ptr;

typedef std::deque< resource_type > res_list;

private:

struct res_equal : std::unary_function< const resource_type&,
bool > {

const std::string s;

res_equal(const std::string& search) : s(search) {}

bool operator()(const resource_type& r) const {
return s == r.name();
}
};

res_list _resources;

public:


//... other stuff, omitting code

void add_resource(const resource_type& r) {
_resources.push_back(r);
}

const_res_ptr find_resource(const std::string& i) const {

typename res_list::const_iterator result =
std::find_if(_resources.begin(), _resources.end(),
res_equal(i));

if(result != _resources.end())
return &(*result);
else
return 0;
}

};

int main() {

/// integer resources ///////////////////////////////////
typedef ResourceMgr< int > iresmgr;

iresmgr mgr;

iresmgr::resource_type r("my int resource", 30);

mgr.add_resource(r);

if(iresmgr::const_res_ptr res_ref = mgr.find_resource("my int resource"))
std::cout << "horray, name: " << res_ref->name() << ", res: " << res_ref->res() << '\n';
else
std::cout << "opps...\n";

///////////////////////////////////////////////////////

/// string resources //////////////////////////////////

typedef ResourceMgr< std::string > sresmgr;

sresmgr smgr;

sresmgr::resource_type r2("my string resource", "HELLO");

smgr.add_resource(r2);

if(sresmgr::const_res_ptr res_ref2 = smgr.find_resource("my string resource"))
std::cout << "horray, name: " << res_ref2->name() << ", res: " << res_ref2->res() << '\n';
else
std::cout << "opps...\n";
//////////////////////////////////////////////////////
return 0;

}



Quote:
Original post by visage
Does anyone have any ideas as to how this can be solved? I really want to keep my resource manager as "vague" as possible, so that anything that is a class can be considered a resource, and I am not stuck with lists of textures, meshes, etc.


The above will instantiate a resource manager type (i don't mean object instance) with a particular type so you can have resource manager for textures, meshes etc given the type at compile time they are separate types of resource managers but least you don't have to write them by hand.

The other option as already pointed out is through type inheritance it where your resource type is an abstract/interface type and for each particular resource you sub-type from it, this will give you a unified interface so you can have one instance of your resource manager holding all kinds of resource instances at run-time, wheather or not this is useful is down to you.

[Edited by - snk_kid on August 29, 2004 4:28:33 AM]

Share this post


Link to post
Share on other sites
Awesome reply snk_kid, total up rating! but unfortunately I am trying to avoid having several managers. I just want one manager for all of my resources. So I could have a vague manager that would manage ints, etc. Thats where my problem is...I cant create any sort of iterator resource that can handle all the different types of resources.

It seems that it may just be simply impossible...

Share this post


Link to post
Share on other sites
Quote:
Original post by visage
I just want one manager for all of my resources. So I could have a vague manager that would manage ints, etc. Thats where my problem is...I cant create any sort of iterator resource that can handle all the different types of resources.

It seems that it may just be simply impossible...


nothing is impossiable [smile], to be honset thou i don't think you need a so called base "CResource" type that has a name to identitfy each type, instead of a list of CResources have a map or hashmap of strings to pointer to any resource type (note the word any), with the help of smart pointers from boost to handle your deletion of dynamically allocated memory and sharing for you correctly.

Here is a simple example of a resource manager that you can create one instance of and store any resouce type that derives from the base type that the resource manager was given as a type parameter. it turns out quite simplified with the combination of templates & inheritance here:


#include <boost\smart_ptr.hpp>

#include <map>
#include <string>
#include <iostream>

//default creation policy, could use a custom creation policy that use a pools instead
template< typename T >
struct default_creator {

static inline T* create() { return new T; }

static inline T* clone(const T& t) { return new T(t); }

};

template < typename BaseResource, template< typename Cloneable > class Creator = default_creator >
class ResourceMgr {
public:

typedef BaseResource resource_type;
typedef boost::shared_ptr<BaseResource> res_ptr;
typedef typename std::map< std::string, res_ptr > res_map;
typedef typename res_map::iterator iterator;
typedef typename res_map::const_iterator const_iterator;

private:

res_map _resources;

public:

static const res_ptr NULL_RESOURCE;

//... other stuff, omitting code
template< typename DerivedResource >
void add_resource(const std::string& res_name, const DerivedResource& r) {

const_iterator result = _resources.find(res_name);

if(result == _resources.end()) {
res_ptr res(Creator<DerivedResource>::clone(r));
_resources[res_name] = res;
}
}

res_ptr find_resource(const std::string& i) {

iterator result = _resources.find(i);

if(result != _resources.end())
return result->second;
else
return NULL_RESOURCE;
}

};

template < typename BaseResource, template< typename Cloneable > class Creator >
const typename ResourceMgr< BaseResource, Creator >::res_ptr
ResourceMgr< BaseResource, Creator >::NULL_RESOURCE;

/////////////////////////////////// END MANAGER TYPE /////////////////////////////////////////////////


///// CLIENT CODE /////////
struct base_resource {

const std::string s;

base_resource(const std::string& name) : s(name) {}

const std::string& test_func() const {
return s;
}

//add virtual or pure virtual methods here
virtual ~base_resource() {}
};

//conreate types override or implement base_resource
struct sound : base_resource {


sound(): base_resource("sound") {}

};

struct texture : base_resource {
texture() : base_resource("texture") {}
};

struct mesh : base_resource {
mesh() : base_resource("mesh") {}
};


int main() {

typedef ResourceMgr< base_resource > bResMgr;

bResMgr mgr;

mgr.add_resource("my sound", sound());
mgr.add_resource("my texture", texture());
mgr.add_resource("my mesh", mesh());

bResMgr::res_ptr res = mgr.find_resource("my texture");

if(res != bResMgr::NULL_RESOURCE)
std::cout << "resource: " << res->test_func() << '\n';
else
std::cout << "opps\n" << '\n';

bResMgr::res_ptr res2 = mgr.find_resource("my sound");

if(res2 != bResMgr::NULL_RESOURCE)
std::cout << "resource: " << res2->test_func() << '\n';
else
std::cout << "opps\n" << '\n';

bResMgr::res_ptr res3 = mgr.find_resource("my mesh");

if(res3 != bResMgr::NULL_RESOURCE)
std::cout << "resource: " << res3->test_func() << '\n';
else
std::cout << "opps\n" << '\n';

return 0;
}



output:

resource: texture
resource: sound
resource: mesh


you can take this further make it a singleton because as you say you only wont one instance of it, also another possiablity instead of returning a smart pointer, still use the smart pointer inside the container to handle delete dynamic memory but return a special "smart" handle type to the caller.

[Edited by - snk_kid on August 29, 2004 6:07:51 AM]

Share this post


Link to post
Share on other sites

This topic is 4855 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.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this