Better QOpenGLWidget by delaying GL code to initializeGL with lambda functions When I am writing OpenGL applications with the QOpenGLWidget class, deciding what code goes into the initializeGL function has always been tricky. One example is when I have a setter that requires an initialized OpenGL context, I have to make sure the OpenGL context is initialized before calling the setter, which is a huge pain.

The following snippet allows me to put GL functions anywhere in the class without worrying whether the OpenGL context is initialized. This is done by wrapping the GL functions with the delayForInit function. It checks whether the OpenGL context is already initialized. If it is, then run the GL functions immediately. If not, then delay the GL functions until initializeGL is called.

A simple usage example is shown at the bottom.

#ifndef OPENGLWIDGET_H
#define OPENGLWIDGET_H

#include &lt;QOpenGLWidget&gt;
#include &lt;functional&gt;

/**
 * @brief The OpenGLWidget class
 */
class OpenGLWidget : public QOpenGLWidget {
    Q_OBJECT
public:
    explicit OpenGLWidget(QWidget* parent = 0)
      : QOpenGLWidget(parent)
      , _isInitialized(false) {}

protected:
    virtual void initializeGL() override {
        for (auto&amp; initFunc : _initFuncs)
            initFunc();
        _initFuncs.clear();
        _isInitialized = true;
    }
    void delayForInit(std::function&lt;void()&gt; func) {
        if (_isInitialized) {
            makeCurrent();
            func();
            doneCurrent();
        } else
            _initFuncs.push_back(func);
    }

private:
    bool _isInitialized;
    std::vector&lt;std::function&lt;void()&gt;&gt; _initFuncs;
};

#endif // OPENGLWIDGET_H

// usage example
class SomeWidget : public OpenGLWidget {
    Q_OBJECT
public:
    SomeWidget() {
        delayForInit([this]() {
            glClearColor(0.f, 0.f, 0.f, 0.f);
            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        });
    }
};
Small nitpicks:

The default value of the parameter QWidget* parent of the constructor is 0. This should be replaced with nullptr.

Either OpenGLWidget or SomeWidget would need to inherit from QOpenGLFunctions to get access to the OpenGL functions used in the example.

The implementation as-is is absolutely not thread-safe. This might not be a concern if your application is guaranteed to be single threaded, but more often than not, applications using advanced graphics end up using multiple threads.

Since the context() member function is publicly accessible, it might be appropriate to make delayForInit adhere to the same level of accessibility.

While delayForInit isn't the worst name, it doesn't really convey what it's doing. deferGLCall or withGLContext might be more fitting.


Design
It's simplicity of usage is great. However, you have no way of enforcing that it will be used at all possible call sites (where appropriate).Another possibility that comes to mind would be to use the State pattern to provide access to the OpenGL functions in form of a member, with 2 states: One that records the OpenGL calls and one that simply forwards them. The call to initializeGL then switches state from recording to forwarding, executing the recorded calls.Though also not foolproof, but which is harder to forget: &quot;I have to use OpenGL calls in a lambda passed to this function.&quot; or &quot;I have to use this member to get access to the OpenGL functions (as they aren't provided directly by default anyways).&quot;?
Example (rough outline, as I currently have no Qt install at hand):
class GLState {
protected:
    QOpenGLWidget&amp; widget;
public:
    explicit GLState(QOpenGLWidget&amp; w) : widget(w) {}
    virtual ~GLState() {}
    
    // define gl functions as needed
    void glClearColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha) = 0;
    void glClear(GLbitfield mask) = 0;
    // ...
};

class ForwardingGLState : public GLState, public QOpenGLFunctions {
    Q_OBJECT // not sure if needed, but probably is
public:
    ForwardingGLState(QOpenGLWidget&amp; w) : GLState(w), QOpenGLFunctions(w.context()) {}
    
    // gl calls implemented by QOpenGLFunctions, nothing to do here
};

class RecordingGLState : public GLState {
    std::vector&lt;std::function&lt;void()&gt;&gt; records;
public:
    RecordingGLState(QOpenGLWidget&amp; w) : GLState(w), records() {}
    
    // either do execution of recorded calls during destructor (might
    // be tricky with exceptions) or implement another virtual function
    // in GLState
    virtual ~RecordingGLState() {
        if(widget.context() == nullptr) return;
        
        widget.makeCurrent();
        for(auto&amp; record : records) {
            record();
        }
        widget.doneCurrent();
    }

    // record gl calls
    // might be able to simplify lambda implementation
    void glClearColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha) {
        records.push_back([&amp;]() {
            widget.context()-&gt;functions()-&gt;glClearColor(red, green, blue, alpha);
        });
    }

    void glClear(GLbitfield mask) {
        records.push_back([&amp;]() {
            widget.context()-&gt;functions()-&gt;glClear(mask);
        });
    }
};


Usage in OpenGLWidget:
class OpenGLWidget : public QOpenGLWidget {
    Q_OBJECT
protected:
    std::unique_ptr&lt;GLState&gt; gl;

public:
    OpenGLWidget(QWidget *parent = nullptr) : QOpenGLWidget(parent), gl(std::make_unique&lt;RecordingGLState&gt;(*this)) {}
    
protected:
    virtual void initializeGL() override {
        // if not (ab-)using the destructor, execute recorded calls here
        gl = std::make_unique&lt;ForwardingGLState&gt;(*this);
    }
};

class SomeWidget : public OpenGLWidget {
    Q_OBJECT
public:
    SomeWidget(QWidget *parent = nullptr) : OpenGLWidget(parent) {
        gl-&gt;glClearColor(0.f, 0.f, 0.f, 0.f);
        gl-&gt;glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    }
};

Yes, it's a lot of boiler plate code inside the GLStates, but after that its mostly done (though some of it could probably be replaced with macros if it gets too bad). Plus side: you can't really get the final usage wrong!