静态转换接口类到内部引擎实现
static_cast interface class to internal engine implementation
我正在开发一个3D引擎,假设我有以下接口类:
class IA {
public:
virtual ~IA() {}
virtual void doSomething() =0;
};
class IB {
public:
virtual ~IB() {}
virtual void bindA( IA* ) =0;
};
如果你想获得类型为"IA"或"IB"的对象,你必须从依赖于正在使用的后端API(例如OpenGL)的工厂获得它们。函数IB::bindA(IA*)需要访问IA实现中的数据,为了实现这一点,它对实现类做了一个static_cast
,然后直接访问它的元素。
我想知道你对static_cast
的这种特殊使用有什么看法,你认为它是糟糕的设计吗?还是你觉得可以?
无论使用什么后端API,引擎都必须提供相同的接口,所以我认为我不能使用虚拟函数来实现这一点,因为我无法事先知道IB从IA需要什么。
谢谢:D
编辑
问题是引擎有以下两个类:
class IHardwareBuffer {
public:
virtual ~IHardwareBuffer() {}
virtual void allocate( .... ) =0;
virtual void upload( .... ) =0;
};
和
class IMesh {
public:
virtual ~IMesh() {}
virtual bindBuffer( IHardwareBuffer* ) =0;
...
};
我"可以"合并IMesh和IHardwareBuffer类在一起,但这不会有太大的意义,因为HardwareBuffer只是一个"哑"的内存与顶点数据在它,和一个网格是一个或两个HardwareBuffers与其他数据周围,如顶点格式,材料等。让它们成为单独的类允许客户端代码有几个网格共享一个共同的HardwareBuffer和类似的东西。
在我看来,从设计的角度来看,这实际上是一个非常糟糕的主意。
如果您使用接口(或模拟接口,因为c++没有这样的语言结构),则使用它们来发布这些数据,这些数据在其他地方需要。因此,如果一个实现IB
的对象必须将IA
转换为某些东西来检索其数据,这显然是一个迹象,要么IA
发布的数据不够,要么实现IA
的对象也应该实现另一个更宽的接口。
很难说哪个选项更好(或者是否有其他选项),因为我们不知道这里的上下文。一般来说,如果真的没有必要,应该避免使用强制类型转换,而且这里显然是没有必要。
编辑:
The engine has to provide the same interface no matter what backend API is being used, so I don't think I could achieve this using virtual functions because I can't know beforehand what is needed by IB from IA.
-这是一个糟糕的设计。
引擎应该以这样的方式编写,即它完全独立于使用它的实现,反之亦然。这就是使用接口、基类和多态性的全部要点:你应该能够编写另一个引擎,将它与现有的引擎交换,并且在实现中不做任何改变的情况下,一切都应该工作。
编辑(回复评论):
我认为,更明确的解决方案是转换到另一个接口,而不是具体实现,即:
class A : public IA, public IInternalA
{
// Implementation
};
// Inside B:
void B::Process(IA * a)
{
IInternalA ia = dynamic_cast<IInternalA *>(a);
if (ia != nullptr)
// Do something
}
这样你仍然可以从实现中分离出来(例如,你可以把它分成两个独立的部分),但是在你的引擎中,所有的类将足够了解彼此以正常工作。
对象有动态类型和静态类型,前者可以在运行时从它的虚函数表中读取,后者可以在源代码中声明。
要基于动态类型安全地强制转换,请使用dynamic_cast
。如果在不查看虚函数表的情况下已经知道了动态类型,那么可以将dynamic_cast
优化为static_cast
。这可能会有意义地提高性能,这样做并没有错,只要它是有效的。
我建议使用引用,而不是指针,因为如果强制转换无效,dynamic_cast
的引用形式会抛出异常。然后你可以这样做:
// Check dynamic type (and throw exception) for debug build only
#ifndef NDEBUG
#define downcast static_cast
#else
#define downcast dynamic_cast
#endif
Iopengl &glenv = downcast< Iopengl & >( myIA );
如果总是知道实际的动态类型而不去vtable(例如全局opengl
标志),那么vtable当然是多余的。你可以用标志和分支来代替虚拟调度来编写整个程序。
如前所述,抽象基类提供接口,派生类调用后端。你的例子有点粗略,但看起来IA和IB是接口,如果你要达到你的目标,你必须定义一个独立于后端的方式,无论实现。无论使用什么后端API,引擎都必须提供相同的接口,所以我认为我不能使用虚拟函数来实现这一点,因为我无法事先知道IB从IA需要什么。
现在我想我明白你的问题了。你有几个几乎没有共同点的后端,你需要在一个隐藏它们差异的引擎中使用它们。
现在的问题是,如果两个类型没有共同之处,它们不应该从一个共同的基继承。当然,像将指针存储在void*
中这样的黑客只是在地毯下扫灰尘。所以我们不要那样做
因此需要为每个后端提供包装器。所有包装器都应该遵循相同的接口,但就其实现而言没有任何共同之处。工厂应该返回一个包装器。
class IBackendWrapper
{
public:
... backend pure virtual functions ...
};
class OpenGLBackendWrapper : public IBackendWrapper
{
public:
... backend virtual function immplementations in terms of OpenGL ...
private:
... OpenGL data ...
};
class X11BackendWrapper : public IBackendWrapper
{
public:
... backend virtual function immplementations in terms of X11...
private:
... X11 data ...
};
class BackendFactory
{
public:
IBackendWrapper* getbackend();
};
现在你的引擎可以使用IBackendWrapper
,而不用担心具体的后端。
- 将函数类成员映射到类本身内部
- Boost Spirit,获取迭代器内部语义动作
- 我不明白为什么我声明一个空的内部结构并将其传递给构造函数
- 内联函数中具有内部链接的全局变量
- 在函数内部的声明中初始化数组,并在外部使用它
- 如何在不知道向量大小的情况下输入向量内部的向量?
- 当使用带有VS2019或VSCode的虚幻引擎4.24.2时,我如何修复这些错误的Intellisense错误
- Unity在虚幻引擎4中的"Vector3.Slerp"等效C++?
- 如何创建从Maya(或类似程序)到虚幻引擎的自定义数据导出插件
- 卷曲bracers内部结构的声明
- 从函数角度看ID到文件路径的内部与外部映射
- spdlog标头仅与外部fmt一起使用.spdlog错误:'内部':不是'fmt'
- 如何在pugixml中获取节点的内部XML
- 使用C链接在函数内部创建C++模板
- 指针没有更新它在void函数内部指向的值
- 方法内部但循环仍得到预期的不合格id错误C++
- 在虚幻引擎中删除NXOpen对象时崩溃
- C++:具有内部链接的正向声明常量
- 引擎节点:未定义的符号:_ZTV6Config
- 静态转换接口类到内部引擎实现