glfwGetWindowUserPointer returns empty std::function when used on class

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();
	}
}

Hello and welcome to the forum!

The issue might be that the GLFW window user pointer is pointing at the temporary Engine::Window instance you create with title and size, while you set the function object of the global instance.

That seems to have been the issue but I don’t really understand why that is since the default constructor of the Window class does not set the user pointer or anything. Regardless, I at least know what the problem is, so thanks

The problem ended up being that the default assignment operator did not reset the window user pointer, obviously, so after overloading that, the issue was fixed. once again, thanks