Reputation: 1811
The question may be hard to understand but the problem is quite simple and I will describe it here in simple words. Right now, my resource managment is:
cResMgr<cTexture> textures;
cResMgr<cSound> sounds;
What I want to do is:
cResMgr resources;
resources.add<cTexture>(...);
resources.get<cSound>(...);
Basically, my resource manager has "get" and "add" functions. I want it that when I call function for cWhatever type first time, it creates a container for it. When it is called for the next times, it is just is there (it's similar to static variable in function)
The question is, how can I implement it? The only solution I can think of is to have every resource deriving from empty base class cResource, so that way I can have one container of pointers to cResource. The problem is, that the resource types aren't mine (they are from external lib)
Any solutions?
Upvotes: 2
Views: 104
Reputation: 17415
You can use a map. Of course, this is resolved at runtime, which kind-of defeats the goal of templates to resolve as much as possible at compile-time. Also, the container will be a bit tricky, because containers don't have some abstract baseclass. I'd use Boost.Any for that. This then looks like this:
template<res_type>
container<res_type>& get()
{
map<type_info, boost::any>::iterator it = map.find(typeid(res_type));
if(it == map.end())
map[typeid(res_type)] = container<res_type>();
boost::any& c = map[typeid(res_type)];
return boost::any_cast<container<res_type> >(c);
}
I didn't compile this, but I hope it gets the point across. Lastly, one question: Do you really have so many different type that all this is worth the hassle or is it curiosity.
Upvotes: 1
Reputation: 28178
You were on the right track with a base class for type erasure purposes, and having your resource manager hold pointers to it's resources. Like you implied, it's an unreasonable burden to make users of your resource system derive their resources from a base class.
So, what you need to do is create a couple class to wrap the resource...
class ResourceBase
{
/*...*/
};
template<typename T>
class Resource : public ResourceBase
{
/* contains a T. T is the user's resource */
};
Then, your resource manager can contain a list of ResourceBase pointers. When a user says resources.get<cSound>("sound7");
you can look up the ResourceBase
pointer (if there is one), downcast it to a Resource<cSound>
and return the cSound
contained.
Upvotes: 1
Reputation: 392833
I don't really know why you wouldn't just use different resourcemanagers per resource type.
Also, if it's okay for the collections to be globally static, why do you need an instance of a resource manager?
Anyways, this should do what you describe:
#include <string>
#include <map>
typedef double cTexture;
typedef std::string cSound;
struct cResMgr
{
template <typename T>
void add(std::string const& id, T const& v) const
{
mapFor<T>()[id] = v;
}
template <typename T>
T& get(std::string const& id) const
{
return mapFor<T>().at(id);
}
private:
template <typename T>
static std::map<std::string, T> mapFor()
{
static std::map<std::string, T> _theMap;
return _theMap;
}
};
int main()
{
cResMgr resources;
resources.add<cTexture>("wall", cTexture {});
resources.get<cSound>("sad_trombone");
}
Upvotes: 2