Pointing to a Function That Is a Class Member - Glfw Setkeycallback

Pointing to a function that is a class member - GLFW setKeycallback

There is a C++ syntax for pointing to class member methods but you cannot pass them to a C style API. C understands function calls and every non-static object method, taking your events as an example, looks like this thinking in C terms: void events(void* this, int, int); meaning that every method apart from the standard arguments also gets a this pointer silently passed.

To make your events C compatible make it static void events(int, int);. This way it will follow the C calling semantics - it will not require a this pointer getting passed. You have to also somehow pass your object to this callback in some other manner (if you need this object's data in the callback).

glfwSetCursorPosCallback to function in another class

You can't pass a class's member function as a function. glfwSetCursorPosCallback it's expecting a function and throwing the error because it gets a member function.

In other words your expected to provide a global function and pass that to glfwSetCursorPosCallback.

If you really want the controls object to get the cursor position callback you could store an instance of Controls in a global variable and pass on the callback to that instance. Something like this:

static Controls* g_controls;

void mousePosWrapper( double x, double y )
{
if ( g_controls )
{
g_controls->handleMouse( x, y );
}
}

Then when you call glfwSetCursorPosCallback you can pass the mousePosWrapper function:

glfwSetCursorPosCallback( window, mousePosWrapper );

How to glfwSetKeyCallback for different classes?

Things get much easier when you step back for a moment and don't think in classes at all. Also a class is a type and types don't really describe a specific state. I think what you actually mean are instances if a class with different internal state.

So your problem then becomes to associate a callback with your state. In your post you wrote the following incomplete callback signature:

keycallback(GLFWwindow *window, int key, int scancode, int action, int mods);

There is missing something, namely the type it returns. Which is void so the full signature is

void GLFWCALL keycallback(
GLFWwindow *window,
int key,
int scancode,
int action,
int mods);

Where GLFWCALL is a not further specified macro that expands into additional type signature qualifiers. You don't have to care about it anymore, than that this is the only type signature you can validly pass as a callback. Now image you'd write something like

class FooState
{
void GLFWCALL keycallback(
GLFWwindow *window,
int key,
int scancode,
int action,
int mods);
};

What would be the type signature of this? Well, when you actually implement it, you're writing it down

void GLFWCALL FooState::keycallback(
GLFWwindow *window,
int key,
int scancode,
int action,
int mods);

Look at this additional FooState::. This is not just some addition to the name or a namespace. It's actually a type modifier. It's like as if you added another parameter to the function (which is what in fact happens), and that parameter is a reference or a pointer (a pointer in the case of C++) to the class instance. This pointer is usually called this. So if you were to look at this function through the eyes of a C compiler (which doesn't know classes) and GLFW is written in C, the signature in fact looks like

void GLFWCALL keycallback(
FooState *this,
GLFWwindow *window,
int key,
int scancode,
int action,
int mods);

And it's pretty obvious that this doesn't fit the needs of GLFW. So what you need is some kind of wrapper that gets this additional parameter there. And where do you get that additional parameter from? Well that would be another variable in your program you have to manage.

Now we can use static class members for this. Within the scope of a class static means a global variable shared by all instances of the class and functions that are within the namespace of the class, but don't work on instances of it (so from a C compiler's perspective they're just regular functions with no additional parameter).

First a base class that gives us a unified interface

// statebase.h
class StateBase
{
virtual void keycallback(
GLFWwindow *window,
int key,
int scancode,
int action,
int mods) = 0; /* purely abstract function */

static StateBase *event_handling_instance;
// technically setEventHandling should be finalized so that it doesn't
// get overwritten by a descendant class.
virtual void setEventHandling() { event_handling_instance = this; }

static void GLFWCALL keycallback_dispatch(
GLFWwindow *window,
int key,
int scancode,
int action,
int mods)
{
if(event_handling_instance)
event_handling_instance->keycallback(window,key,scancode,action,mods);
}
};

// statebase.cpp
#include "statebase.h"
// funny thing that you have to omit `static` here. Learn about global scope
// type qualifiers to understand why.
StateBase * StateBase::event_handling_instance;

Now the purely abstract virtual function is the trick here: Whatever kind of class derived from StateBase is referenced by the event_handling_instance by use of a virtual dispatch it will end up being called with the function implementation of the class type of that instance.

So we can now declare FooState being derived from StateBase and implement the virtual callback function as usual (but omit that GLFWCALL macro)

class FooState : BaseState
{
virtual void keycallback(
GLFWwindow *window,
int key,
int scancode,
int action,
int mods);
};
void FooState::keycallback(
GLFWwindow *window,
int key,
int scancode,
int action,
int mods)
{
/* ... */
}

To use this with GLFW register the static dispatcher as callback

glfwSetKeyCallback(window, StateBase::keycallback_dispatcher);

And to select a specific instance as active callback handler you call its inherited void StateBase::setEventHandling() function.

FooState state;
state.setEventHandling();

A few parting words of advice:

If you've not been familiar with these concepts of OOP in C++ and the intricate how types and instances interact you should not do any OOP programming, yet. OOP was once claimed to make things easier to understand, while in fact the shrewd ways it got implemented in C++ actually makes one brain hurt and proliferates very bad style.

Here's an exercise for you: Try to implement what you try to achieve without using OOP. Just use structs without member functions in them (so that they don't become classes in disguise), try to think up flat data structures and how to work with them. It's a really important skill which people who jump directly into OOP don't train a lot.

Function was not declared in scope (GLFW, OpenGL, Slackware)

To use the C++ member function void key_callback(), as a parameter to the C-implemented library function glfwSetKeyCallback(), you should declare it as static, but outside of the class declaration first. So try something like this for your window.h.

class Window;
static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods);

class Window {
private:
// fields here

public:
// other member functions here

private:
friend void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods);
};

And then in window.cpp:

static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) {
Window* win = (Window*) glfwGetWindowUserPointer(window);
win->m_Keys[key] = action != GLFW_RELEASE;
}

I haven't written in this glfw library, so my answer might not be sufficient for your purposes. Have a look at these questions: Pointing to a function that is a class member , Is it possible to declare a friend function as static?

Alternative to conversion constructor for template parameter

Actually, I don't really see the point of using CRTP here. What's the problem with just storing the pointer to the template argument directly?

template <class T>
class Controller : public BC {
public:
static Controller& getInstance(T * target = nullptr){
static Controller instance(target);
return instance;
}

static void keyCallback(GLFWwindow* window, int key, int scancode,
int action, int mods){
getInstance().target_->keyCallbackImpl(window, key, scancode, action, mods);
}
private:
Controller(T* target) : target_(target) { }
//This is the wrapped input handler
T* target_;
};


Related Topics



Leave a reply



Submit