我应该为我的不透明对象使用整数ID还是指针? [英] Should I use integer ID or pointers for my opaque objects?

查看:259
本文介绍了我应该为我的不透明对象使用整数ID还是指针?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在一些图形API(DirectX9和DirectX11)之上编写一个抽象层,我想要你的意见。



传统上,我会创建一个基类对于我想要抽象的每个概念。

所以在典型的OO时代,我会有一个类Shader和2个子类DX9Shader和DX11Shader。



我会重复纹理的过程等等,当我需要实例化它们时,我有一个抽象工厂,将根据当前的图形API返回适当的子类。

在RAII之后,返回的指针将被封装在std :: shared_ptr中。



到目前为止很好,但在我的例子中,这个方法有一些问题:


  1. 我需要提供一个公开接口,用于封装两种API(和将来的其他API)的功能。

  2. 派生类存储在单独的DLL中(一个用于DX9,一个用于DX11等),并且在客户端具有shared_ptr是一个诅se:退出时,图形dll被卸载,如果客户端仍然有



  3. 这是一个由于从卸载的DLL调用代码而导致的崩溃。促使我重新设计我做事情的方式:
    我认为我可以只是返回原始指针的资源,并有图形API干净本身后,但仍然有客户端和接口悬空指针的问题问题。
    我甚至认为手动引用计数像COM,但我认为这将是一个倒退(纠正我,如果我错了,来自shared_ptr世界,手动引用计数看起来原始)。



    然后我看到了Humus的工作,所有他的图形类都用整数ID表示(很像OpenGL)。
    创建一个新对象只返回其整数ID,并在内部存储指针;它完全不透明!



    代表抽象的类(例如DX9Shader等...)都隐藏在设备API后面,这是唯一的接口。

    如果想设置一个纹理,它只是一个调用device-> SetTexture(ID)的问题,其余发生在幕后。



    失败的是,API的隐藏部分是膨胀的,有很多锅板代码需要使其工作,我不是一个do-it-all类的粉丝。



    任何想法/想法?

    解决方案

    你说主要的问题是DLL卸载,同时仍具有指向其内部的指针。嗯... 不要这样做。您有一个类实例,该成员是在该DLL中实现的



    因此,您需要对如何处理这些类实例负责。使用这种抽象。正如你需要对从DLL加载的任何代码负责:在卸载DLL之前,必须清除来自DLL的内容。你怎么做取决于你。你可以有一个内部引用计数,对于DLL返回的每个对象都增加,并且只有卸载所有引用对象后,卸载DLL。或者什么,真的。



    毕竟,即使你使用这些不透明的数字或任何,如果你调用这些API函数之一,卸载?糟糕...所以它不真的买你任何保护。你必须以任何一种方式负责。



    你可能没有想到的数字方法的缺点是:




    • 实际上减少了知道对象实际上是什么的能力。 。 API调用可能会失败,因为您传递的数字实际上不是对象。或者更糟的是,如果你将一个着色器对象传递给一个需要纹理的函数,会发生什么?也许我们在谈论一个函数,它需要一个着色器和一个纹理,你不小心忘了参数的顺序? C ++的规则不允许代码甚至编译,如果那些是对象指针。但是用整数?都很好;您只会收到运行时错误。


    • 性能。每个API调用都必须在一个哈希表或某事中查找这个数字,以获得一个实际的指针。如果它是一个哈希表(即:一个数组),那么它可能是相当小。但它仍然是一个间接。


    • 缺少RAII和其他范围界定机制。当然,你可以写一个 shared_ptr -esque对象,创建和删除它们。但你不必这样做,如果你使用一个实际的指针。




    值得。


    I'm writing an abstraction layer on top of some graphics API (DirectX9 and DirectX11) and I would like your opinion.

    Traditionally I would create a base class for each concept I want to abstract.
    So in typical OO fashion I would have for example a class Shader and 2 subclasses DX9Shader and DX11Shader.

    I would repeat the process for textures, etc... and when I need to instantiate them I have an abstract factory that will return the appropriate subclass depending on the current graphics API.
    Following RAII, the returned pointer would be encapsulated in a std::shared_ptr.

    So far so good but in my case there are a few problems with this approach:

    1. I need to come up with a public interface that encapsulate the functionality of both APIs (and other APIs in the future).
    2. The derived class are stored in separate DLLs (one for DX9, one for DX11 etc...) and having a shared_ptr to them in the client is a curse: on exit the graphic dlls are unloaded and if the client still has a shared_ptr to one of the graphics objects boom, crash due to calling code from unloaded DLL.

    This prompted me to re-design the way I do things: I thought I could just return raw pointers to the resources and have the graphics API clean after itself but there's still the issue of dangling pointers on the client side and the interface problem. I even considered manual reference counting like COM but I thought that would be a step backwards (correct me if I'm wrong, coming from the shared_ptr world, manual reference counting seems primitive).

    Then I saw the work of Humus where all his graphics classes are represented by integer IDs (much like what OpenGL does). Creating a new object only returns its integer ID, and stores the pointer internally; it's all perfectly opaque!

    The classes that represent the abstraction (such as DX9Shader etc...) are all hidden behind the device API which is the only interface.
    If one wants to set a texture, it's just a matter of calling device->SetTexture(ID) and the rest happens behind the scenes.

    The downfall is that the hidden part of the API is bloated, there is a lot of boiler plate code required to make it work and I'm not a fan of a do-it-all class.

    Any ideas/thoughts ?

    解决方案

    You say that the main problem is that a DLL is unloaded while still having a pointer to its internals. Well... don't do that. You have a class instance, who's members are implemented in that DLL. It is fundamentally an error for that DLL to be unloaded so long as those class instances exist.

    You therefore need to be responsible in how you use this abstraction. Just as you need to be responsible with any code you load from a DLL: stuff that comes from the DLL must be cleaned up before you unload the DLL. How you do that is up to you. You could have an internal reference count that gets incremented for every object the DLL returns and only unload the DLL after all referenced objects go away. Or anything, really.

    After all, even if you use these opaque numbers or whatever, what happens if you call one of those API functions on that number when the DLL is unloaded? Oops... So it doesn't really buy you any protection. You have to be responsible either way.

    The downsides of the number method that you may not be thinking about are:

    • Reduced ability to know what an object actually is. API calls can fail because you passed a number that isn't really an object. Or worse, what happens if you pass a shader object into a function that takes a texture? Maybe we're talking about a function that takes a shader and a texture, and you accidentally forget the order of the arguments? The rules of C++ wouldn't allow that code to even compile if those were object pointers. But with integers? It's all good; you'd only get runtime errors.

    • Performance. Every API call will have to look this number up in a hashtable or something to get an actual pointer to work with. If it's a hashtable (ie: an array), then it's probably fairly minor. But it's still an indirection. And since your abstraction seems very low-level, any performance loss at this level can really hurt in performance-critical situations.

    • Lack of RAII and other scoping mechanisms. Sure, you could write a shared_ptr-esque object that would create and delete them. But you wouldn't have to do that if you were using an actual pointer.

    It just doesn't seem worthwhile.

    这篇关于我应该为我的不透明对象使用整数ID还是指针?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆