I'm writing a simple wrapper in C++ around GLFW and OpenGL, as an exercice. I have a class Window
and a class Renderer
. The Window
class owns a Renderer
member.
Window
sets the GLFW context in its constructor and run the main loop, and call for its Renderer
member to draw. Renderer
sets the buffers in its constructor, and does the OpenGL things.
The problem I've got is that OpenGL calls require to have an OpenGL context available. If I were to just initialise the Renderer
before calling the constructor of Window
, the constructor of Renderer
would be called before the GLFW context would be created.
What I did is that I instead store a unique_ptr
to a Renderer
in Window
, and I call std::make_unique<Renderer>
in the constructor of Window
. Then, in the destructor, I call std::unique_ptr::reset()
before destroying the GLFW context, so that I can make the OpenGL calls to free stuff with a still valid context.
class Window
{
public:
Window()
{
//initialising 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 object should already be initialised in the body of the constructor, which is not the case here. I feel I might have done some dependency between Renderer
and Window
backwards or that my general architecture is wrong. I would rather rely on the constructor and destructors being called at the right moment based on the scope and not on me manually triggering it.
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).