Note: This answer expands on https://gamedev.stackexchange.com/a/150166/35344.
Let's take a player entity that has a few methods for movement:
struct Player {
void moveLeft() {
xVel = -1
}
void moveRight() {
xVel = 1
}
};
Then, with a setup such as the following, we can bind the movement methods to our callback manager (called Window
here). The binding is performed in initialization, but you'd also perhaps want to perform this when new objects that need to listen to keyboard events are instantiated, or even when the player switches their keybindings (all though other approaches might be more suitable for that, however those are beyond the scope of this question).
int main() {
Window window;
setupGlfw(window);
Player player;
window.addBinding(KEY_LEFT, std::bind(&Player::moveLeft, &player));
window.addBinding(KEY_RIGHT, std::bind(&Player::moveRight, &player));
// Begin gameloop
}
The key here is std::bind
, which takes a member function pointer, and an instance of the owning class, and creates an std::function<void()>
for us, that we register as a callback to the specific key event.
The parameter Callback
that addBinding
accepts is a bound function call. Its purpose is to allow generic objects and functions to be bound to the same event handler (Window
), as we could just as well pass around static member functions, functions in the global scope or even lambdas to the callback manager.
Note that our current implementation leaves a few details out, including memory management, which are left as an exercise to the reader (as they are way beyond the scope of this question).