I am making a window wrapper class for c++ and I am trying to handle callback functions for glfw. The window wrapper class contains an std::function which stores, in this case, a callback for glfwSetWindowCloseCallback. The class also has a static function which is passed to glfwSetWindowCloseCallback and correspondingly calls the std::function onCloseCallback when that event occurs by using the GLFWwindow userpointer. onCloseCallback is set by the member function onClose. In the main.cpp file, onClose is called in order to set the onCloseCallback function. Through debugging I have made sure that it is properly set. However, when setting a breakpoint inside the static onClose function which is supposed to call the onCloseCallback, onCloseCallback ends up being empty. What is weirder is that if I make the window wrapper object inside the main.cpp file a pointer, this problem goes away.
// main.cpp
#include <iostream>
#include "Engine.hpp"
Engine::Game game;
Engine::Window window;
int main()
{
game = Engine::Game();
window = Engine::Window("game", 1280, 720);
window.setContext();
game.initGl();
window.onClose([]() {
window.close();
game.close();
});
game.run();
printf("Programmed ended\n");
system("PAUSE");
return 0;
}
void Engine::Game::update()
{
window.update();
}
void Engine::Game::render()
{
window.render();
}
// Engine.hpp
#pragma once
#include <GL/glew.h>
#include <glfw/glfw3.h>
#include <exception>
#include <iostream>
#include <vector>
#include <string>
#include <functional>
namespace Engine
{
class Game
{
public:
Game();
void initGl();
void run();
void clean();
bool isRunning();
void close();
private:
bool running = false;
void update();
void render();
};
// ---------- Game Class ----------
Game::Game() {
if (!glfwInit()) {
fprintf(stderr, "GLFW failed to initialize\n");
throw std::runtime_error("GLFW failed to initialize");
}
running = true;
}
void Game::initGl() {
glewExperimental = true;
if (glewInit() != GLEW_OK) {
fprintf(stderr, "GLEW failed to initialize\n");
glfwTerminate();
throw std::runtime_error("GLEW failed to initialize");
}
}
void Game::clean() {
glfwTerminate();
}
void Game::run() {
while (isRunning())
{
update();
render();
}
clean();
}
bool Game::isRunning() {
return running;
}
void Game::close() {
running = false;
}
class Window
{
public:
Window();
Window(std::string title, int width, int height);
void setContext();
void update();
void render();
void close();
//Events
void registerEvents();
void onClose(const std::function<void()>& callback);
private:
GLFWwindow* glfwWindow;
std::function<void()> onCloseCallback;
static void onClose(GLFWwindow* window);
};
// ---------- Window Class ----------
Window::Window() { }
Window::Window(std::string title, int width, int height) {
glfwWindowHint(GLFW_SAMPLES, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindow = glfwCreateWindow(width, height, title.c_str(), NULL, NULL);
if (glfwWindow == NULL) {
fprintf(stderr, "Failed to open GLFW window. Check OpenGL compatibility");
glfwTerminate();
throw std::runtime_error("Failed to open GLFW window. Check OpenGL compatibility");
}
glfwSetWindowUserPointer(glfwWindow, static_cast<void*>(this));
registerEvents();
}
void Window::setContext() {
glfwMakeContextCurrent(0);
glfwMakeContextCurrent(glfwWindow);
}
void Window::update() {
glfwPollEvents();
}
void Window::render() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glClearColor(0.0f, 0.5f, 0.2f, 1.0f);
glfwSwapBuffers(glfwWindow);
}
void Window::close() {
glfwSetWindowShouldClose(glfwWindow, 1);
//glfwDestroyWindow(glfwWindow);
}
void Window::registerEvents() {
glfwSetWindowCloseCallback(glfwWindow, onClose);
}
void Window::onClose(const std::function<void()>& callback) {
onCloseCallback = callback;
glfwSetWindowCloseCallback(glfwWindow, onClose);
}
void Window::onClose(GLFWwindow* window) {
Window* self = static_cast<Window*>(glfwGetWindowUserPointer(window));
if (self->onCloseCallback) self->onCloseCallback();
}
}