5

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

传统上,我会为每个要抽象的概念创建一个基类。
因此,在典型的 OO 方式中,我将拥有一个类 Shader 和 2 个子类 DX9Shader 和 DX11Shader。

我会重复纹理等的过程......当我需要实例化它们时,我有一个抽象工厂,它将根据当前的图形 API 返回适当的子类。
在 RAII 之后,返回的指针将被封装在 std::shared_ptr 中。

到目前为止一切都很好,但就我而言,这种方法存在一些问题:

  1. 我需要提供一个公共接口来封装这两个 API(以及未来的其他 API)的功能。
  2. 派生类存储在单独的 DLL 中(一个用于 DX9,一个用于 DX11 等),并且在客户端中有一个 shared_ptr 是一个诅咒:退出时,图形 dll 被卸载,如果客户端仍然有一个 shared_ptr 到由于从卸载的 DLL 调用代码,图形对象之一繁荣、崩溃。

这促使我重新设计我做事的方式:我认为我可以只返回指向资源的原始指针并让图形 API 自行清理,但仍然存在客户端悬空指针和接口问题的问题。我什至考虑过像 COM 这样的手动引用计数,但我认为这将是一个倒退(如果我错了,请纠正我,来自 shared_ptr 世界,手动引用计数似乎很原始)。

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

代表抽象的类(例如 DX9Shader 等)都隐藏在设备 API 后面,这是唯一的接口。
如果要设置纹理,只需调用 device->SetTexture(ID) 即可,其余的在幕后进行。

缺点是 API 的隐藏部分过于臃肿,需要大量样板代码才能使其工作,而且我不喜欢全能类。

有什么想法/想法吗?

4

3 回答 3

3

有关系吗?对于对象的用户来说,它只是一个不透明的句柄。它的实际实现类型无关紧要,只要我可以将句柄传递给您的 API 函数并让它们对对象进行处理。

您可以轻松地更改这些句柄的实现,所以现在就让它变得更容易。

只需将句柄类型声明为指针或整数的 typedef,并确保所有客户端代码都使用 typedef 名称,然后客户端代码不依赖于您选择表示句柄的特定类型。

现在寻求简单的解决方案,如果/当您因为太简单而遇到问题时,请更改它。

于 2011-11-30T09:56:28.777 回答
3

您说主要问题是 DLL 被卸载,同时仍有指向其内部的指针。嗯...不要那样做。您有一个类实例,其成员在该 DLL中实现。只要这些类实例存在,卸载 DLL基本上就是一个错误。

因此,您需要对如何使用此抽象负责。正如您需要对从 DLL 加载的任何代码负责一样:必须在卸载 DLL 之前清理来自 DLL 的内容。你如何做到这一点取决于你。您可以有一个内部引用计数,该计数会随着 DLL 返回的每个对象而增加,并且仅在所有引用的对象消失后才卸载 DLL。或者任何东西,真的。

毕竟,即使您使用这些不透明的数字或其他任何东西,如果您在卸载 DLL 时在该数字上调用其中一个 API 函数会发生什么?哎呀...所以它并没有真正为您购买任何保护。无论哪种方式,您都必须负责。

您可能没有考虑到的数字方法的缺点是:

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

  • 表现。每个 API 调用都必须在哈希表或其他东西中查找此数字以获取实际可用的指针。如果它是一个哈希表(即:一个数组),那么它可能相当小。但这仍然是间接的。而且由于您的抽象看起来非常低级,因此在此级别的任何性能损失都会在性能关键的情况下真正受到伤害。

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

这似乎不值得。

于 2011-11-30T10:06:35.773 回答
0

关于你的 p。2:客户端总是在库之前卸载。

每个进程都有它的库依赖树,.exe 作为树根,用户 Dll 在中间级别,系统库在低级别。进程从低层加载到高层,树根(exe)最后加载。从根目录开始卸载进程,最后卸载低级库。这样做是为了防止您正在谈论的情况。

当然,如果你手动加载/卸载库,这个顺序会改变,你有责任保持指针有效。

于 2011-11-30T09:59:10.773 回答