Encapsulating an OpenGL object in C++98/03 requires obeying the C++ rule of 3. This means adding a copy constructor, copy assignment operator, and destructor.
However, copy constructors should logically copy the object. And copying an OpenGL object is a non-trivial undertaking. Equally importantly, it's almost certainly something that the user does not wish to do.
So we will instead make the object non-copyable:
class BufferObject
{
public:
BufferObject(GLenum target, GLsizeiptr size, const void *data, GLenum usage)
{
glGenBuffers(1, &object_);
glBindBuffer(target, object_);
glBufferData(target, size, data, usage);
glBindBuffer(target, 0);
}
~BufferObject()
{
glDeleteBuffers(1, &object_);
}
//Accessors and manipulators
void Bind(GLenum target) const {glBindBuffer(target, object_);}
GLuint GetObject() const {return object_;}
private:
GLuint object_;
//Prototypes, but no implementation.
BufferObject(const BufferObject &);
BufferObject &operator=(const BufferObject &);
};
The constructor will create the object and initialize the buffer object's data. The destructor will destroy the object. By declaring the copy constructor/assignment without defining them, the linker will give an error if any code tries to call them. And by declaring them private, only members of BufferObject
will even be able to call them.
Note that BufferObject
does not retain the target
passed to the constructor. That is because an OpenGL buffer object can be used with any target, not just the one it was initially created with. This is unlike texture objects, which must always be bound to the target they were initially created with.
Because OpenGL is very dependent on binding objects to the context for various purposes, it is often useful to have RAII-style scoped object binding as well. Because different objects have different binding needs (some have targets, others do not), we have to implement one for each object individually.
class BindBuffer
{
public:
BindBuffer(GLenum target, const BufferObject &buff) : target_(target)
{
buff.Bind(target_);
}
~BindBuffer()
{
glBindBuffer(target_, 0);
}
private:
GLenum target_;
//Also non-copyable.
BindBuffer(const BindBuffer &);
BindBuffer &operator=(const BindBuffer &);
};
BindBuffer
is non-copyable, since copying it makes no sense. Note that it does not retain access to the BufferObject
it binds. That is because it is unnecessary.