我一直在编写一个实用程序,用作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>
类型的Signature
, void()
:这些对象可以存储一个对象,您可以传入所需的对象,例如:
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/