C++ OpenGL Function Call Wrapping

Error handling in a state-based C API such as OpenGL in C++ can sometimes be a bit of a pain. To help me with my own graphics engine, I created this simple method of wrapping the OpenGL function pointers to report errors as they happen.

For my method to work, you’ll likely need to load your own OpenGL functions rather than using a loading library. When you do, you’ll need to store the addresses of the loaded functions into a map along with a string representation of the function name.

std::unordered_map<std::intptr_t,std::string> g_glFunctionNameMap;

For example, here’s my function that loads an OpenGL function pointer from the function name:

template<typename FuncPtrType>
void get_gl_func_address(const std::string& name, FuncPtrType& funcPtr)
{
    void *p = (void *)wglGetProcAddress(name.c_str());
    if(p == 0 || (p == (void*)0x1) || (p == (void*)0x2) || (p == (void*)0x3) || (p == (void*)-1) )
    {
        HMODULE module = LoadLibraryW(L"opengl32.dll");
        p = (void *)GetProcAddress(module, name.c_str());
    }

    funcPtr = reinterpret_cast<FuncPtrType>(p);

    g_glFunctionNameMap[reinterpret_cast<intptr_t>(funcPtr)] = name;
}

Not the last part where I add the function names to my map.

Now let’s define the actual wrapper parts:

#define glCall(function, ...) glCallImpl(__FILE__, __LINE__, function, __VA_ARGS__)

First we’ll use the pre-processor to define a new function named glCall, this is the wrapper for all other OpenGL function calls.

Then we’ll define a template function which takes a pointer to a function and checks for any OpenGL errors. If it finds any, it can report them as having occurred in the provided function call.

template<typename FuncPtrType>
bool check_gl_errors(FuncPtrType& lastFunc, const std::string& filename, const std::uint_fast32_t line)
{
    GLenum error = glGetError();
    if(error != GL_NO_ERROR)
    {
        std::cerr << "***ERROR*** in (" << filename << ": " << std::to_string(line) << ") " << g_glFunctionNameMap[reinterpret_cast<intptr_t>(lastFunc)];
        do
        {
            std::string errorStr = "Unknown Error";
            switch(error)
            {
            case GL_INVALID_ENUM:
                errorStr = "Invalid Enumerator";
                break;
            case GL_INVALID_VALUE:
                errorStr = "Invalid Value";
                break;
            case GL_INVALID_OPERATION:
                errorStr = "Invalid Operation";
                break;
            case GL_STACK_OVERFLOW:
                errorStr = "Stack Overflow";
                break;
            case GL_STACK_UNDERFLOW:
                errorStr = "Stack Underflow";
                break;
            case GL_OUT_OF_MEMORY:
                errorStr = "Out of Memory";
                break;
            case GL_INVALID_FRAMEBUFFER_OPERATION:
                errorStr = "Invalid Framebuffer Operation";
                break;
            case GL_CONTEXT_LOST:
                errorStr = "Context Lost";
                break;
            }
            std::cerr << "\n\t--" << errorStr << "--";
        } while((error = glGetError()) != GL_NO_ERROR);

        return false;
    }
    return true;
}

Straight-forward, loop through any errors and report them. Return true or false depending on whether we found any errors.

So our #define calls one of the following functions:

template<typename glFunction, typename... Params>
auto glCallImpl(const char* filename, const std::uint_fast32_t line, glFunction function, Params... params)
->typename std::enable_if_t<std::is_same_v<void, decltype(function(params...))>, bool>
{
    function(std::forward<Params>(params)...);
    return check_gl_errors(function, filename, line);
}

This first one is only called if the return type of the OpenGL function pointer is void. If the OpenGL function pointer returns a value, this glCallImpl is used instead:

template<typename glFunction, typename ReturnType, typename... Params>
auto glCallImpl(const char* filename, const std::uint_fast32_t line, glFunction function, ReturnType& returnValue, Params... params)
->typename std::enable_if_t<!std::is_same_v<void, decltype(function(params...))>, bool>
{
    returnValue = function(std::forward<Params>(params)...);
    return check_gl_errors(function, filename, line);
}

Then we can use it in all our OpenGL calls.

When returning a value:

GLuint vs; // filled with the return value
if(!glCall(glCreateShader,vs,GL_VERTEX_SHADER))
{
    /* handle the error */
}

And when it doesn’t:

if(!glCall(glCompileShader,vs))
{
    /* handle error */
}

I haven’t needed it yet, but returning the actual error value, i.e. the GLenum may be more practical (it boolean compares to false when there’s no error); but I’ve not found a case where an error reported by OpenGL gives me room to do something.

I hope someone finds this useful in developing their own game or game engine. I’ve done the same thing with OpenAL.

Leave a Comment