As an exercise, I'm writing a simple wrapper in C++ around GLFW and OpenGL. I have two classes: Window
and Renderer
, where Window
owns an instance of Renderer
.
The Window
class sets up the GLFW context in its constructor, runs the main loop, and uses its Renderer
member to handle drawing. The Renderer
class sets up the buffers in its constructor and handles OpenGL function calls.
The issue I'm facing is that OpenGL requires a valid context for any function calls. If I initialize Renderer
before the Window
constructor is called, the Renderer
constructor will run before the GLFW context is created.
To avoid this, I store a unique_ptr
to Renderer
in the Window
class and instantiate it using std::make_unique<Renderer>
inside the Window
constructor. Then, in the Window
destructor, I call std::unique_ptr::reset()
before destroying the GLFW context to ensure that OpenGL calls can still be made to release resources while the context is valid.
class Window
{
public:
Window()
{
// Initializing GLFW and creating an OpenGL context
// ...
m_renderer = std::make_unique<Renderer>();
}
~Window()
{
m_renderer.reset();
glfwTerminate();
}
int run()
{
while(...)
{
m_renderer->draw();
// ...
}
return 0;
}
private:
std::unique_ptr<Renderer> m_renderer;
// ...
}
class Renderer
{
public:
Renderer() { /* Set buffers */ }
~Renderer() { /* Free buffers */ }
draw() { glDrawElements(...); /* ... */ }
private:
// ...
}
int main()
{
Window window();
return window->run();
}
I understand that the Renderer
object should already be initialized when calling the Window
constructor, which is not the case here. I feel I might have inverted the dependency between Renderer
and Window
, or my overall architecture might be wrong. I would rather rely on the constructor and destructor being called at the right moment based on the scope than triggering it manually.
What would be a better solution?
I suggest that you create a separate class, call it GLFWInit
that does the GLFW initialization and calls glfwTerminate()
in its destructor. Then you have two options:
Embed an object of type GLFWInit
in your Window
class. Place the member early, but at the least before the m_renderer
member.
Derive your Window
class from GLFWInit
.
Both methods ensure that GLFW is initialied before m_renderer
and torn down after it. Then you do not even have to make the renderer a pointer and can embed the member directly (if that is feasible).