D到C函数中的类方法回调

时间:2014-04-02 20:10:39

标签: methods callback d glfw

我在D中编写一个简单的轻量级引擎。对于输入调用,我使用GLFW3。有问题的库使用回调将输入事件发送到程序。

我想要的是使用类中的方法作为回调函数,而不是函数。事实证明这很困难(就像在C ++中一样)。我相信有一种优雅的方式可以做到这一点,但这就是我现在的方式。

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

以下是回调设置功能的定义和类型:

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

我想要做的是创建一个属于类的方法。有没有办法做到这一点?

我尝试的解决方案是在static中包含的extern (C)方法,这可用于调用它,但后来我可以(显然)无法访问this或任何其他方法,否定了演习的重点。

提前致谢。

2 个答案:

答案 0 :(得分:0)

我这样做的方法是拥有一个指向类的指针的静态映射,如:

static YourWindowClass[GLFWwindow*] mappings;

然后,在构造函数中,一旦获得GLFWwindow指针,将其添加到:

mappings[m_Window.window] = this;

现在,将静态extern(C)函数用作回调。当它从C获取指针时,在该映射数组中查找您的类引用,然后继续并通过它调用成员函数,转发参数。

这是一个额外的步骤,但是因为它看起来不像回调允许你将用户定义的数据传递给它(BTW,注意所有的lib编写者:用户定义的void *到回调是sooooo有用的,你应该尽可能地做!),但是因为它没有做到这一点,关联数组是下一个最好的事情。

答案 1 :(得分:0)

好吧,我已经把它弄清楚了。我使用的解决方案是Singleton类InputManagerRenderWindow的实例使用以下函数附加到它上面。 InputManager然后为接收事件的function()创建匿名RenderWindow,然后调用处理实际事件的函数。

接下来的想法是,听众将自己附加到InputManager并接收他们请求的RenderWindow的键盘事件。

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
    }
}

像魅力一样工作,现在要弄清楚如何正确地正确地附上听众。

对于那些好奇的人,可以在https://github.com/Adel92/Mage2D找到完整的源代码及其设置方法。我希望它可以帮助处于类似位置的其他人进行回调。