I want to write a simple RAII wrapper for OpenGL objects (textures, frame buffers, etc.) I have noticed, that all glGen*
and glDelete*
functions sh
Why reinvent the wheel? There is a neat solution using std::unique_ptr
, which already provides the needed functionality, so you need to only write the traits (!):
template<void (*func)(GLuint)>
struct gl_object_deleter {
struct pointer { // I wish we could inherit from GLuint...
GLuint x;
pointer(std::nullptr_t = nullptr) : x(0) {}
pointer(GLuint x) : x(x) {}
operator GLuint() const { return x; }
friend bool operator == (pointer x, pointer y) { return x.x == y.x; }
friend bool operator != (pointer x, pointer y) { return x.x != y.x; }
};
void operator()(GLuint p) const { func(p); }
};
void delete_texture(GLuint p) { glDeleteTextures(1, &p); }
void delete_shader(GLuint p) { glDeleteShader(p); }
// ...
typedef std::unique_ptr<void, gl_object_deleter<delete_texture>> GLtexture;
typedef std::unique_ptr<void, gl_object_deleter<delete_shader>> GLshader;
// ...
Most the Create*
functions return an array through their argument, which is inconvenient when you allocate your objects one-by-one. It is possible to define a set of creation routines for single instances:
GLuint glCreateTextureSN(GLenum target) { GLuint ret; glCreateTextures(target, 1, &ret); return ret; }
GLuint glCreateBufferSN() { GLuint ret; glCreateBuffers(1, &ret); return ret; }
// ...
Some OpenGL functions, like glCreateShader
can be used directly. Now we can use it as follows:
GLtexture tex(glCreateTextureSN(GL_TEXTURE_2D));
glTextureParameteri(tex.get(), GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
// ...
GLtexture tex2 = std::move(tex); // we can move
tex2.reset(); // delete
return tex2; // return...
One downside is that you cannot define an implicit cast to GLuint
, so you must call get()
explicitly. But, on a second thought, preventing an accidental cast to GLuint
is not such a bad thing.
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);
};
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).
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.
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;
};
An OpenGLObject<VertexArrayObjectTraits>
would hold a VAO.