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

我想拥有一套功能,使我能够轻松地传递键代码以及想要在按下或释放键时触发的功能。

我认为这里的一切都正确,但是我正在尝试在Game类中使用它,但出现以下错误:

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

Game.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);

KeyManager.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;
        }
    }
}

最佳答案

当您使用非static成员函数的地址时,您不会获得指向函数的“普通”指针,而是获得了指向成员函数的指针:成员函数不仅将显式指定的参数用作参数,而且还采用了指向对象的隐式指针(变成this)作为参数。因此,Game::testKey的类型为而不是 void(*)(),而是void (Game::*)(),表明您在调用此函数时仍需要传递Game对象。

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

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

当您确实不需要将函数发送到的对象时,还可以将函数static生成,该函数会导致不使用隐式对象的函数。但是,通常实际上确实需要该对象,仅使函数static不能提供足够的上下文。

假设上下文需要Game对象,那么KeyManager将使用std::function<void(char)>报告有关键事件的信息(我还添加了char参数来获取使用哪个键的信息):
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()返回的函数对象将忽略其他参数。

关于c++ - 功能指针作为功能参数,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/19889590/

10-14 04:35