将 void* 投射到更高的东西的安全方法?

发布于 2024-11-19 03:27:34 字数 804 浏览 1 评论 0原文

我有一个通用类来管理各种类型的资源,但由于我不想为每个 T 创建一个 ResourceManager 实例(因此为每个类型 T 都有一个资源管理器),所以我必须ResourceManager 类未知的 T 类型。

我通过保存 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]);
}

我使用模板专门化向类引入不同的加载器:

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

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

我知道这很丑陋,但现在没有办法解决它。我可以在专门的 Load 方法内部引入静态映射,但这样我就无法将资源的生命周期绑定到 ResourceManager 对象的生命周期,而这是一个基本功能。

但由于这有点危险(因为这些 void* 指针可以是任何东西),我想至少在运行时检查转换是否有效,这样我就可以在不导致应用程序崩溃的情况下对其做出反应。

我该怎么做?

I've got a generic class that manages resources of all kinds of types, but since I don't want to create an instance of ResourceManager for every T there is (thus having one resource manager for each type T), I have to make the type of T unknown to the ResourceManager class.

I do this by saving a map of void* pointers and converting them back to the required format if someone requests a certain type out of a templated Load() method;

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

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

I use template specialization to introduce different Loaders to the class:

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

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

I am aware that this is ugly, but there is no way around it right now. I could introduce static maps in the inside of the specialized Load methods, but that way I can't bind the lifetime of the resources to the lifetime of an ResourceManager object, which is an essential feature.

But since this is somewhat dangerous (since those void* pointers can be anything), I'd like to at least check at runtime if the conversion is going to work, so I can react to it without having the application crash.

How can I do this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

江挽川 2024-11-26 03:27:34

除非您存储指示每个指针实际类型的附加信息,否则无法检查您可以将 void* 转换为什么。

一种更“C++ 方式”来完成您想要的操作,是从抽象基类 Resource 派生每个资源类,并将指向 Resource 的指针映射存储在资源管理器中。然后你可以使用dynamic_cast转换为所需的类型,如果指针指向错误类型的对象,这将返回NULL。或者(取决于您想要做什么)您可以简单地返回一个 Resource* 指针并使用虚函数来实现每个资源的功能。

There is no way to check what you can cast void* to, unless you store additional information that indicates the actual type with each pointer.

A more "C++ way" to do what you want is to derive each resource class from an abstract base class Resource, and store a map of pointers to Resource in your resource manager. Then you can use dynamic_cast<T*> to convert to the required type, and this will return NULL if the pointer is to an object of the wrong type. Or (depending on what you want to do) you can simply return a Resource* pointer and use virtual functions to implement the functionality of each resource.

一世旳自豪 2024-11-26 03:27:34

如果您扩展保存的值类型,则可以轻松地做到这一点 - 使其成为一个还保存 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_ptrvoid> 以节省资源。

You can easily do this, if you extend your saved value type - make it a struct that also saves a type_info object:

#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);
  }
}

This is similar to how I do it, but I use a shared_ptr<void> to save the resources.

脸赞 2024-11-26 03:27:34

(我确信有关 void 指针的许多其他问题已经回答了这个问题,但我们开始......)

但是因为这有点危险
(因为这些 void* 指针可以是
任何东西),我至少想检查一下
在运行时如果转换正在进行
去工作,这样我就可以对它做出反应而无需
应用程序崩溃。

你无法检查。这就是 void* 指针的问题。您不知道它们指向什么,并且您不能(不允许)在不知道其类型的情况下检查它们指向的内存。

如果你有一个void*,你必须事先知道它真正指向的是什么,然后进行适当的转换。

(I'm sure this is already answered by many other questions about void pointers, but here we go ...)

But since this is somewhat dangerous
(since those void* pointers can be
anything), I'd like to at least check
at runtime if the conversion is going
to work, so I can react to it without
having the application crash.

You cannot check. This is the thing about void* pointers. You don't have a clue what they are pointing to, and you cannot (are not allowed to) inspect the memory they point to without knowing its type.

If you have a void* you simply must know beforehand what it is really pointing to and then cast appropriately.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文