1

我一直在编写一个实用程序来作为 GLFW 键输入的包装器。

我想要一组函数,使我能够轻松地传递键代码和我希望在按下或释放键时触发的函数。

我认为这里的一切都是正确的,但我试图在我的 Game 类中使用它并且我收到以下错误:

game.cpp(27): error C2664: 'void KeyManager::press(int,void *)' : 无法将参数 2 从 'void (__thiscall Game::* )(void)' 转换为 'void *' 1> 那里没有可以进行这种转换的上下文

游戏.cpp:

void Game::testKey()
{
    std::cout << "Key Up Pressed" << std::endl;
}

void Game::init()
{
    //  KEY MANAGER:
    KeyManager::initialize();
    KeyManager::press(GLFW_KEY_UP, &Game::testKey);

密钥管理器.cpp:

#include <map>
#include <vector>

#include "global.h"
#include "key_manager.h"

namespace KeyManager
{
    std::map<int, int>                      keyAction;
    std::map<int, std::vector<void(*)()>>   pressFunctions;
    std::map<int, std::vector<void(*)()>>   releaseFunctions;

    static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
    {
        keyAction[key] = action;

        if (keyAction[key] == GLFW_PRESS)
        {
            for (int i = 0; i != pressFunctions[key].size(); ++i)
            {
                (*pressFunctions[key].at(i))();
            }
        }

        if (keyAction[key] == GLFW_RELEASE)
        {
            for (int i = 0; i != releaseFunctions[key].size(); ++i)
            {
                (*releaseFunctions[key].at(i))();
            }
        }
    }

    void initialize()
    {
        glfwSetKeyCallback(window, key_callback);
    }

    void press(int keyCode, void(*listener)())
    {
        pressFunctions[keyCode].push_back(listener);
    }

    void release(int keyCode, void(*listener)())
    {
        releaseFunctions[keyCode].push_back(listener);
    }

    bool held(int keyCode)
    {
        if (keyAction[keyCode] == GLFW_PRESS || keyAction[keyCode] == GLFW_REPEAT)
        {
            return true;
        } else {
            return false;
        }
    }
}
4

1 回答 1

4

当您获取非static成员函数的地址时,您不会得到指向函数的“正常”指针,而是指向成员函数的指针:成员函数不仅将显式指定的参数作为参数,而且还采用隐式指向对象的指针(变成this)作为参数。因此,你的类型Game::testKey不是不是 void(*)()而是而是void (Game::*)(),说明你Game在调用这个函数的时候还需要传入一个对象。

如果您可以控制被调用函数的类型,则最好使用函数对象的模板参数,或者,如果您需要存储多个这些对象,则std::function<Signature>使用合适的类型Signature,在您的情况下可能void():这些objects 可以存储一个对象,您可以传入所需的对象,例如:

std::function<void()> fun(std::bind(&Game::testKey, this));

std::bind()是一个工厂函数,它以一个函数作为它的第一个参数和一组参数,这些参数指定函数在调用时应该获取的参数。在上面的示例中,Game::testKey函数获取绑定到的第一个参数this(当然,您可以使用指向要使用的Game对象的任何指针)。

当你真的不需要一个对象来发送函数时,你也可以制作static一个函数,它会产生一个不带隐式对象的函数。然而,通常该对象实际上是必需的,并且仅仅制作该函数static并不能提供足够的上下文。

假设Game上下文需要该对象,该对象KeyManager将用于std::function<void(char)>报告关键事件(我还添加了一个char参数来获取使用了哪个键的anof):

class KeyManager
{
    std::vector<std::function<void(char)>> handlers;
public:
    void addHandler(std::function<void(char)> h) {
        handlers.push_back(h);
    }
    //...
};

稍后,当您有合适的Game对象时,您可以添加一个处理函数:

KeyManager m;
Game           g;
m.addHandler(std::bind(&Game::keyDown, &game));

请注意,Game::keyDown实际上一个使用Gane对象并且没有额外的参数:这没关系,因为从返回的函数对象std::bind()将忽略额外的参数。

于 2013-11-10T12:36:31.163 回答