OpenGL 对象的 RAII 包装器
我想为我注意到的 OpenGL 对象(纹理、帧缓冲区等)编写一个简单的 RAII 包装器,所有 glGen*
和 glDelete*
函数共享相同的签名,所以我的第一次尝试是这样的:
I want to write a simple RAII wrapper for OpenGL objects (textures, frame buffers, etc.) I have noticed, that all glGen*
and glDelete*
functions share the same signature, so my first attempt was like this:
typedef void (__stdcall *GLGenFunction)(GLsizei, GLuint *);
typedef void (__stdcall *GLDelFunction)(GLsizei, const GLuint *);
template <GLGenFunction glGenFunction, GLDelFunction glDelFunction>
class GLObject
{
GLuint m_name;
public:
GLObject()
{
glGenFunction(1, &m_name);
}
~GLObject()
{
glDelFunction(1, &m_name);
}
GLuint getName() {return m_name;}
};
typedef GLObject<glGenTextures, glDeleteTextures> GLTexture;
它适用于纹理,但不适用于帧缓冲区:glGenFramebuffers
和 glDeleteFramebuffers
函数地址在编译时未知,不能用作模板参数.所以我做了第二个版本:
It works fine for textures, but fails for frame buffers: glGenFramebuffers
and glDeleteFramebuffers
function addresses are not known at compile time, and cannot be used as template arguments. So I made second version:
class GLObjectBase
{
GLuint m_name;
GLDelFunction m_delFunction;
public:
GLObjectBase(GLGenFunction genFunc, GLDelFunction delFunction)
: m_delFunction(delFunction)
{
genFunc(1, &m_name);
}
GLuint getName()
{
return m_name;
}
protected:
~GLObjectBase()
{
m_delFunction(1, &m_name);
}
};
class GLFrameBuffer : public GLObjectBase
{
public:
GLFrameBuffer() : GLObjectBase(glGenFramebuffers, glDeleteFramebuffers) {}
};
但我不喜欢它,因为我必须在每个实例中存储在运行时不会改变的 del 函数指针.
But I don't like it since I have to store del function pointer in each instance that will not change at run-time.
如何制作仅在每个实例中存储对象名称的包装类,而无需创建一堆几乎复制粘贴的类?
How do I make wrapper class that stores only object name in each instance without resorting to create a bunch of almost copy-pasted classes?
我可以这样做:
template <int N>
class GLObject2
{
GLuint m_name;
static GLDelFunction glDelFunction;
public:
GLObject2(GLGenFunction genFunction, GLDelFunction delFunc)
{
genFunction(1, &m_name);
if ( glDelFunction == nullptr )
glDelFunction = delFunc;
ASSERT(glDelFunction == delFunc);
}
GLuint getName() {return m_name;}
protected:
~GLObject2()
{
glDelFunction(1, &m_name);
}
};
template <int N>
GLDelFunction GLObject2<N>::glDelFunction = nullptr;
class GLTexture: public GLObject2<1>
{
public:
GLTexture(): GLObject2<1>(glGenTextures, glDeleteTextures) {}
};
class GLRenderBuffer: public GLObject2<2>
{
public:
GLRenderBuffer(): GLObject2<2>(glGenRenderbuffers, glDeleteRenderbuffers) {}
};
谁能提出更优雅的解决方案?
Can anyone suggest more elegant solution?
推荐答案
真的,您会像 C 程序员一样思考这个问题.您正在使用 C++,因此请按照 C++ 程序员的方式解决它.使用特征类:
Really, you're thinking about this like a C programmer. You're using C++, so solve it the way a C++ programmer would. With a traits class:
struct VertexArrayObjectTraits
{
typedef GLuint value_type;
static value_type Create();
static void Destroy(value_type);
};
就像一个适当的 C++ 特征类,我们让每个对象声明它自己的 value_type
.这将允许您使其适应不使用 GLuint
的 OpenGL 对象,例如 同步对象(尽管创建/销毁界面无论如何都不适合它们,所以你可能不应该打扰).
Like a proper C++ traits class, we have each object declare it's own value_type
. This will allow you to adapt it to OpenGL objects that don't use GLuint
s, like sync objects (though the Create/Destroy interface wouldn't be good for them anyway, so you probably shouldn't bother).
因此,您为每种类型的 OpenGL 对象编写了一个特征类.您的 Create
和 Destroy
函数会将调用适当地转发到 C API.
So you write one traits class for each type of OpenGL object. Your Create
and Destroy
functions will forward the calls on to the C API appropriately.
这样做之后,您只需要一个围绕那些接口的 RAII 包装器:
After doing that, all you need is a RAII-wrapper around those interfaces:
template<typename T>
class OpenGLObject
{
public:
OpenGLObject() : m_obj(T::Create()) {}
~OpenGLObject() {T::Destroy(m_obj);}
operator typename T::value_type() {return m_obj;}
private:
typename T::value_type m_obj;
};
OpenGLObject
将持有 VAO.
相关文章