功能指针作为功能参数

时间:2013-11-10 12:17:25

标签: c++ vector map key glfw

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

我希望有一组函数可以让我轻松传递密钥代码和我希望在按下或释放密钥时触发的函数。

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

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

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

1 个答案:

答案 0 :(得分:4)

当您获取非static成员函数的地址时,您不会返回指向函数的“正常”指针,而是指向成员函数的指针:成员函数执行此操作不仅将显式指定的参数作为参数,而且它们还将一个隐式指针作为参数(对象变为this)。因此,Game::testKey的类型不是 void(*)(),而是void (Game::*)(),表明您仍然需要传入Game个对象在调用此函数时。

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

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()返回的函数对象将忽略其他参数。