user1873947
user1873947

Reputation: 1811

Class that creates new containers for type known from its template member function's call

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

Answers (3)

Ulrich Eckhardt
Ulrich Eckhardt

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

David
David

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

sehe
sehe

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

Related Questions