Вопрос может быть трудным для понимания, но проблема довольно проста, и я опишу его здесь простыми словами.
Прямо сейчас, мое управление ресурсами:
cResMgr<cTexture> textures;
cResMgr<cSound> sounds;
Что я хочу сделать, это:
cResMgr resources;
resources.add<cTexture>(...);
resources.get<cSound>(...);
По сути, мой менеджер ресурсов имеет функции «получить» и «добавить». Я хочу, чтобы при первом вызове функции для любого типа он создавал для нее контейнер. Когда он вызывается в следующий раз, он просто есть (он похож на статическую переменную в функции)
Вопрос в том, как я могу это реализовать? Единственное решение, которое я могу придумать, — это чтобы каждый ресурс был производным от пустого базового класса cResource, чтобы у меня был один контейнер указателей на cResource. Проблема в том, что типы ресурсов не мои (они из внешней библиотеки)
Какие-либо решения?
Я действительно не знаю, почему бы вам не использовать разные менеджеры ресурсов для разных типов ресурсов.
Кроме того, если коллекции могут быть статичными на глобальном уровне, зачем вам пример менеджера ресурсов?
В любом случае, это должно делать то, что вы описываете:
#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");
}
Вы были на правильном пути с базовым классом для стирания типов, и ваш менеджер ресурсов держал указатели на его ресурсы. Как вы и предполагали, необоснованное бремя заставлять пользователей вашей системы ресурсов извлекать свои ресурсы из базового класса.
Итак, что вам нужно сделать, это создать пару классов, чтобы обернуть ресурс …
class ResourceBase
{
/*...*/
};
template<typename T>
class Resource : public ResourceBase
{
/* contains a T. T is the user's resource */
};
Затем ваш менеджер ресурсов может содержать список указателей ResourceBase. Когда пользователь говорит resources.get<cSound>("sound7");
Вы можете посмотреть вверх ResourceBase
указатель (если есть) уменьшить его до Resource<cSound>
и вернуть cSound
содержатся.
Вы можете использовать карту. Конечно, это решается во время выполнения, что в некоторой степени противоречит цели шаблонов — максимально разрешать во время компиляции. Кроме того, контейнер будет немного сложнее, потому что контейнеры не имеют некоторого абстрактного базового класса. Я бы использовал Boost.Any для этого. Тогда это выглядит так:
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);
}
Я не скомпилировал это, но надеюсь, что это понятно. Наконец, один вопрос: у вас действительно так много разных типов, что все это стоит хлопот или это любопытство.