2
\$\begingroup\$

I created a universal OpenGL object RAII wrapper class, that only takes care of object creation and destruction. Here's my code and reasoning behind it:

I first wrote a class that would take glCreate/Delete*() function pointers as constructor arguments, but I quickly realized that's not the right way to go. I'd have to store the glDelete*() function pointer in each object (waste of memory + dereference overhead). Additionally, OpenGL functions have three different forms (see specializations section, further in my code), so that would require 3 different pointer types - ugly. I decided to change my approach.

So, here I am with this template code. As far as I'm concerned, there should be no unnecessary OpenGL call overhead. The class is standard layout, only contains the object ID and there's no virtual stuff involved, so that seems pretty nice. Also, classes wrapping different OpenGL object types cannot be assigned to each other, because template arguments differ.

I'd like to hear your remarks according my code, the class design and the reasoning behind it. Improvement ideas are also welcome. Thanks in advance.

This is the main code:

/**
    \brief Serves as a RAII wrapper for OpenGL objects
    \note This class provides no OpenGL error checking - this is up to derived classes.
    \template T is object type as in glObjectLabel
*/
template <GLenum T>
class gl_object
{
    public:
        //! Object type used for labeling
        const static GLenum object_type = T;

        inline gl_object( );
        inline explicit gl_object( GLenum target );

        inline ~gl_object( );

        // Deleted copy constructor and copy assignment operator
        gl_object( const gl_object &src ) = delete;
        gl_object &operator=( const gl_object &rhs ) = delete;

        // Move semantics with source invalidation
        gl_object( gl_object &&src );
        gl_object &operator=( gl_object &&rhs );

        //! Allows casting to object's ID (GLuint)
        inline operator GLuint( ) const noexcept;

        //! Returns object's ID
        inline GLuint id( ) const noexcept;

    protected:
        //! The object ID
        GLuint m_id;
};

//! Move constructor with source invalidation
template <GLenum T>
gl_object<T>::gl_object( gl_object<T> &&src ) :
    m_id( src.m_id )
{
    src.m_id = 0;
}

//! Move assignment operator with source invalidation
template <GLenum T>
gl_object<T> &gl_object<T>::operator=( gl_object<T> &&rhs )
{
    // Prevent self-move
    if ( this != &rhs )
    {
        m_id = rhs.m_id;
        rhs.m_id = 0;
    }

    return *this;
}

// Allows cast to GLuint
template <GLenum T>
gl_object<T>::operator GLuint( ) const noexcept
{
    return m_id;
}

// Used for acquiring object's ID
template <GLenum T>
GLuint gl_object<T>::id( ) const noexcept
{
    return m_id;
}

Then, there are many very similar template specializations. Here are the most interesting ones:

// Specializations for GL_BUFFER
template <>
gl_object<GL_BUFFER>::gl_object( )
{
    glCreateBuffers( 1, &m_id );
}

template <>
gl_object<GL_BUFFER>::~gl_object( )
{
    glDeleteBuffers( 1, &m_id );
}

// Specializations for GL_TEXTURE
template <>
gl_object<GL_TEXTURE>::gl_object( GLenum target )
{
    glCreateTextures( target, 1, &m_id );
}

template <>
gl_object<GL_TEXTURE>::~gl_object( )
{
    glDeleteTextures( 1, &m_id );
}

// Specializations for GL_SHADER
template <>
gl_object<GL_SHADER>::gl_object( GLenum type )
{
    m_id = glCreateShader( type );
}

template <>
gl_object<GL_SHADER>::~gl_object( )
{
    glDeleteShader( m_id );
}

// Specializations for GL_PROGRAM
template <>
gl_object<GL_PROGRAM>::gl_object( )
{
    m_id = glCreateProgram( );
}

template <>
gl_object<GL_PROGRAM>::~gl_object( )
{
    glDeleteProgram( m_id );
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.