Question

I'm writing a simple, lightweight engine in D. For the input calls I use GLFW3. The library in question uses callbacks to send input events to the program.

What I would like is to use a method from a class as the callback function, rather than a function. This is proving difficult (just as it is in C++). I believe there is an elegant way to do it, but this is how I got it right now.

public void initialise(string logPath) {
    [...]
    m_Window = new RenderWindow();
    m_Window.create();

    // Lets set up the input loop.
    GLFWkeyfun keyCB = function(GLFWwindow* win, int key, int scancode, int action, int mods) {
        printf("Got key event: %d:%d:%d:%d\n");
        RenderWindow rw = Root().getRenderWindow();

        switch (key) {
            case KeyboardKeyID.Q:
                glfwSetWindowShouldClose(win, true);
                break;

            case KeyboardKeyID.H:
                if (rw.hidden) {
                    rw.show();
                } else {
                    rw.hide();
                }
                break;

            default:
                break;
        }
    };
    glfwSetKeyCallback(m_Window.window, keyCB);
}

Here is the definition of the callback setting function and type:

extern (C) {
    alias              GLFWkeyfun = void function(GLFWwindow*, int, int, int, int);
    GLFWkeyfun         glfwSetKeyCallback(GLFWwindow*, GLFWkeyfun);
}

What I would like to do instead, is create a method that is part of the class. Is there any way to do this?

A solution I tried was a static method wrapped around in extern (C), this worked for calling it, but then I could (obviously) not access this or any other methods, which defeats the point of the exercise.

Thanks in advance.

Était-ce utile?

La solution 2

Well, I have figured it out my own. The solution I went with was a Singleton class InputManager. Instances of RenderWindow attach themselves to it with the following function. The InputManager then creates an anonymous function() for the RenderWindow that receives events, which then calls a function that handles the actual event.

The idea is then that listeners attach themselves to the InputManager and receive keyboard events for the RenderWindow they requested.

class InputManager {
    private static InputManager m_Instance;
    private RenderWindow[] m_Watched;
    private KeyboardListener[][RenderWindow] m_KeyListeners;

    public void recvKeyEvent(GLFWwindow* w, int k, int c, int a, int m) {
        writeln("Received key: ", k);
    }

    public void watch(RenderWindow win) {
        if (!isWatched(win)) {
            // Relay the key callbacks onto the InputManager.
            GLFWkeyfun keyCB = function(GLFWwindow* w, int k, int c, int a, int m) {
                InputManager().recvKeyEvent(w, k, c, a, m);
            };
            glfwSetKeyCallback(win.window, keyCB);
        }
    }

    private bool isWatched(RenderWindow win) {
        foreach(RenderWindow w; m_Watched) {
            if (win == w) {
                return true;
            }
        }

        return false;
    }

    public static InputManager opCall() {
        if (m_Instance is null) {
            m_Instance = new InputManager();
        }

        return m_Instance;
    }

    private this() {
        // nothing
    }
}

Works like a charm, now to figure out how to properly attach listeners elegantly.

For those curious, the full source code with how this is set up can be found at https://github.com/Adel92/Mage2D. I hope it helps someone else in a similar position with callbacks.

Autres conseils

The way I'd do it is to have a static map of the pointers to the class, so like:

static YourWindowClass[GLFWwindow*] mappings;

Then, in the constructor, once you get a GLFWwindow pointer, add it right in:

mappings[m_Window.window] = this;

Now, make the static extern(C) function to use as the callback. When it gets a pointer from C, look up your class reference in that mappings array and then go ahead and call the member function through that, forwarding the arguments.

So a bit of an extra step, but since it doesn't look like the callback lets you pass user-defined data to it (BTW, attention all lib writers: user-defined void* to the callbacks is sooooo useful, you should do it whenever possible!), but since it doesn't do that the associative array is the next best thing.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top