Безопасный способ выбросить пустоту * во что-то более высокое? - PullRequest
3 голосов
/ 09 июля 2011

У меня есть универсальный класс, который управляет ресурсами всех типов, но так как я не хочу создавать экземпляр ResourceManager для каждого T, который есть (таким образом, имея один менеджер ресурсов для каждого типа T), ядолжен сделать тип T неизвестным для класса ResourceManager.

Я делаю это, сохраняя карту указателей void * и преобразуя их обратно в требуемый формат, если кто-то запрашивает определенный тип из шаблонного метода Load ();

template <typename T>
T* Load(const std::string &location)
{
    //do some stuff here

    //everybody take cover!!!
    return static_cast<T*>(m_resources[location]);
}

Iиспользуйте специализацию шаблонов, чтобы ввести в класс различных загрузчиков:

template<>
AwesomeType* Load(const std::string &location)
{
    //etc.

    return static_cast<AwesomeType*>(m_resources[location]);
}

Я знаю, что это некрасиво, но сейчас нет никакого способа обойти это.Я мог бы представить статические карты внутри специализированных методов Load, но таким образом я не могу связать время жизни ресурсов со временем жизни объекта ResourceManager, что является важной функцией.

Но так как этоявляется несколько опасным (так как указатели void * могут быть любыми), я хотел бы по крайней мере проверить во время выполнения, будет ли преобразование работать, поэтому я могу реагировать на него, не вызывая сбой приложения.

Как я могу это сделать?

Ответы [ 3 ]

7 голосов
/ 09 июля 2011

Невозможно проверить, к чему вы можете привести void*, если только вы не сохраните дополнительную информацию, которая указывает фактический тип с каждым указателем.вывести каждый класс ресурсов из абстрактного базового класса Resource и сохранить карту указателей на Resource в вашем менеджере ресурсов.Затем вы можете использовать dynamic_cast<T*> для преобразования в требуемый тип, и это вернет NULL, если указатель на объект неправильного типа.Или (в зависимости от того, что вы хотите сделать) вы можете просто вернуть указатель Resource* и использовать виртуальные функции для реализации функциональности каждого ресурса.

6 голосов
/ 09 июля 2011

Вы можете легко сделать это, если вы расширите свой сохраненный тип значения - сделайте его структурой, которая также сохраняет объект type_info:

#include <type_info>

struct ResourceInfo
{
  std::type_info const& info;
  void* ptr;
};

// ...

// just to give you the general idea
template<class Res>
void CacheResource(std::string const& location, Res* res)
{
  ResourceInfo ri = { typeid(Res), res };
  m_resources.insert(std::make_pair(location, ri));
}

template<class Res>
Res* Load(std::string const& location)
{
  map_type::const_iterator res_it = m_resources.find(location);
  if(res_it != m_resources.end())
  {
    if(typeid(Res) != res_it->second.info)
    {
      throw SorryBuddyWrongResourceType(some_info_here);
    }
    return static_cast<Res*>(res_it->second.ptr);
  }
}

Это похоже на то, как я это делаю, но я использую shared_ptr<void> для сохранения ресурсов.

5 голосов
/ 09 июля 2011

(я уверен, что на этот вопрос уже есть ответы на многие другие вопросы о пустых указателях, но мы здесь ...)

Но так как это несколько опасно (поскольку эти пустые * указатели могут быть что угодно) хотелось бы хотя бы проверить во время выполнения, если происходит преобразование работать, чтобы я мог реагировать на это без после сбоя приложения.

Вы не можете проверить. Это вещь о void * указатели. Вы не понимаете, на что они указывают, и вы не можете (не можете) проверять память, на которую они указывают, не зная ее типа.

Если у вас есть void*, вы просто должны заранее знать, на что он действительно указывает, а затем разыграть его соответствующим образом.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...