我正在一些图形 API(DirectX9 和 DirectX11)之上编写一个抽象层,我想听听您的意见。
传统上,我会为每个我想要抽象的概念创建一个基类。
因此,在典型的 OO 方式中,我将拥有一个 Shader 类和 2 个子类 DX9Shader 和 DX11Shader。
我会重复纹理等的过程...当我需要实例化它们时,我有一个抽象工厂,它将根据当前的图形 API 返回适当的子类。
遵循 RAII,返回的指针将封装在 std::shared_ptr 中。
到目前为止一切顺利,但就我而言,这种方法存在一些问题:
- 我需要提出一个公共接口来封装这两个 API(以及未来的其他 API)的功能。
- 派生类存储在单独的 DLL 中(一个用于 DX9,一个用于 DX11 等...),并且在客户端中对它们拥有一个 Shared_ptr 是一种诅咒:在退出时,图形 DLL 会被卸载,并且如果客户端仍然有一个 Shared_ptr图形对象之一boom,由于从卸载的 DLL 调用代码而崩溃。
这促使我重新设计我的做事方式:
我以为我可以将原始指针返回到资源,然后让图形 API 清理干净,但仍然存在客户端悬空指针和接口问题。
我什至考虑过像 COM 这样的手动引用计数,但我认为这将是一种倒退(如果我错了,请纠正我,来自shared_ptr 世界,手动引用计数似乎很原始)。
然后我看到了 Humus 的工作,其中他的所有图形类都由整数 ID 表示(很像 OpenGL 所做的)。
创建一个新对象仅返回其整数ID,并在内部存储指针;一切都是完全不透明的!
代表抽象的类(例如 DX9Shader 等)都隐藏在设备 API 后面,这是唯一的接口。
如果想要设置纹理,只需调用 device->SetTexture(ID) 即可,其余的都在幕后发生。
缺点是 API 的隐藏部分过于臃肿,需要大量样板代码才能使其工作,而且我不喜欢全能类。
有什么想法/想法吗?
您说主要问题是 DLL 被卸载,但仍然有指向其内部的指针。出色地...不要那样做。你有一个类实例,它的成员是实施的在该 DLL 中。它从根本上来说是一个error只要这些类实例存在,该 DLL 就会被卸载。
因此,您需要对如何使用此抽象负责。正如你需要负责一样any从 DLL 加载的代码:在卸载 DLL 之前,必须清除来自 DLL 的内容。如何做到这一点取决于您。您可以有一个内部引用计数,该计数对于 DLL 返回的每个对象都会递增,并且仅在所有引用的对象消失后才卸载 DLL。或者任何东西,真的。
毕竟,即使您使用这些不透明的数字或其他什么,如果您在卸载 DLL 时对该数字调用这些 API 函数之一,会发生什么情况?哎呀...所以它并不能真正为您带来任何保护。无论哪种方式你都必须负责。
您可能没有考虑到的数字方法的缺点是:
降低了解物体实际含义的能力is。 API 调用可能会失败,因为您传递的数字并不是真正的对象。或者更糟糕的是,如果将着色器对象传递给采用纹理的函数会发生什么?也许我们正在讨论一个需要着色器和纹理的函数,而您不小心忘记了参数的顺序?如果这些是对象指针,C++ 的规则甚至不允许该代码编译。但是对于整数呢?都很好;你只会得到运行时错误。
表现。每个 API 调用都必须在哈希表或其他内容中查找该数字,以获得要使用的实际指针。如果它是一个哈希表(即:一个数组),那么它可能相当小。但这仍然是一个间接的。由于您的抽象看起来级别非常低,因此在性能关键的情况下,此级别的任何性能损失都会真正受到损害。
缺乏 RAII 和其他范围机制。当然,你可以写一个shared_ptr
-esque 对象将创建和删除它们。但如果您使用实际的指针,则不必这样做。
这似乎不值得。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)