想想你的基本GLUT课程。它们只是从main方法运行并包含回调,如`glutMouseFunc(MouseButton),其中MouseButton是方法的名称。
我所做的是将主文件封装到一个类中,这样MouseButton不再是一个静态函数,而是一个实例。但这样做会给我一个编译错误:
错误2错误C3867:'StartHand :: MouseButton':函数调用缺少参数列表;使用'& StartHand :: MouseButton'创建指向成员c:\ users \ angeleyes \ documents \ visual studio 2008 \ projects \ capstone ver 4 \ starthand.cpp的指针388 IK Engine
由于类非常庞大,因此无法提供代码示例。
我尝试过使用this->MouseButton
但是会出现同样的错误。不能为回调提供指向实例函数的指针吗?
答案 0 :(得分:5)
正如错误消息所示,您必须使用&StartHand::MouseButton
语法来获取指向成员函数的指针(ptmf);这只是作为语言的一部分强制执行的。
当使用ptmf时,你正在调用的函数,在这种情况下为glutMouseFunc,也必须期望获得一个ptmf作为回调,否则使用你的非静态MouseButton将无法工作。相反,一种常见的技术是回调与用户提供的void*
上下文一起工作,该上下文可以是实例指针 - 但执行回调的库必须明确允许此参数。确保匹配外部库所需的ABI(下面的 handle_mouse 函数)也很重要。
由于glut 不允许用户提供的上下文,您必须使用其他机制:将对象与glut的当前窗口关联。然而,它提供了获取“当前窗口”的方法,并且我已经使用它来将void*
与窗口相关联。然后,您只需创建一个trampoline来进行类型转换并调用该方法。
机械:
#include <map>
int glutGetWindow() { return 0; } // make this example compile and run ##E##
typedef std::pair<void*, void (*)(void*,int,int,int,int)> MouseCallback;
typedef std::map<int, MouseCallback> MouseCallbacks;
MouseCallbacks mouse_callbacks;
extern "C" void handle_mouse(int button, int state, int x, int y) {
MouseCallbacks::iterator i = mouse_callbacks.find(glutGetWindow());
if (i != mouse_callbacks.end()) { // should always be true, but possibly not
// if deregistering and events arrive
i->second.second(i->second.first, button, state, x, y);
}
}
void set_mousefunc(
MouseCallback::first_type obj,
MouseCallback::second_type f
) {
assert(obj); // preconditions
assert(f);
mouse_callbacks[glutGetWindow()] = MouseCallback(obj, f);
//glutMouseFunc(handle_mouse); // uncomment in non-example ##E##
handle_mouse(0, 0, 0, 0); // pretend it's triggered immediately ##E##
}
void unset_mousefunc() {
MouseCallbacks::iterator i = mouse_callbacks.find(glutGetWindow());
if (i != mouse_callbacks.end()) {
mouse_callbacks.erase(i);
//glutMouseFunc(0); // uncomment in non-example ##E##
}
}
示例:
#include <iostream>
struct Example {
void MouseButton(int button, int state, int x, int y) {
std::cout << "callback\n";
}
static void MouseButtonCallback(
void* self, int button, int state, int x, int y
) {
static_cast<Example*>(self)->MouseButton(button, state, x, y);
}
};
int main() {
Example obj;
set_mousefunc(&obj, &Example::MouseButtonCallback);
return 0;
}
请注意,您不再直接调用 glutMouseFunc ;它作为 [un] set_mousefunc 的一部分进行管理。
以防万一不清楚:我已经重写了这个答案,所以它应该适合你,这样就避免了C / C ++链接问题的争论。它将按原样编译和运行(没有过剩),并且只需稍作修改即可使用过剩:评论或取消注释标记为##E##
的4行。
答案 1 :(得分:2)
不,指向实例函数的指针不能给予期望某个签名的函数指针的回调函数。他们的签名是不同的。它不会编译。
通常,此类API允许您将void *作为“context”参数传递。你在那里传递你的对象,并编写一个包装函数,它将上下文作为回调。包装器将它强制转换回您正在使用的任何类,并调用相应的成员函数。
答案 2 :(得分:1)
您无法使用实例1替换静态回调。当呼叫者呼叫你的回叫时,它会调用什么样的实例?换句话说,调用者如何传递正式的“this”参数?
解决方案是使用静态回调存根并将实例作为参数传递,这意味着被调用者必须接受在调用回调时将传回的任意pvoid。在存根中,您可以调用非静态方法:
class C {
void f() {...}
static void F(void* p) {
C* pC = (C*)p;
pC->f();
}
}
C* pC = ...;
someComponent.setCallback(&C::F, pC);
答案 3 :(得分:1)
与每个人似乎都在说的相反,你绝对 CAN 使用非静态成员函数作为回调方法。它需要专门用于获取指向非静态成员的指针的特殊语法,以及在类的特定实例上调用该函数的特殊语法。有关所需语法的讨论,请参阅here。
以下示例代码说明了其工作原理:
#include <cstdlib>
#include <string>
#include <iostream>
#include <vector>
#include <sstream>
#include <algorithm>
using namespace std;
class Operational
{
public:
Operational(int value) : value_(value) {};
string FormatValue() const ;
private:
int value_;
};
string Operational::FormatValue() const
{
stringstream ss;
ss << "My value is " << value_;
return ss.str();
}
typedef string(Operational::*FormatFn)() const; // note the funky syntax
Operational make_oper(int val)
{
return Operational(val);
}
int main()
{
// build the list of objects with the instance callbacks we want to call
Operational ops[] = {1, 2, 3, 5, 8, 13};
size_t numOps = sizeof(ops)/sizeof(ops[0]);
// now call the instance callbacks
for( size_t i = 0; i < numOps; ++i )
{
// get the function pointer
FormatFn fn = &Operational::FormatValue;
// get a pointer to the instance
Operational* op = &ops[i];
// call the callback on the instance
string retval = (op->*fn)();
// display the output
cout << "The object @ " << hex << (void*)op << " said: '" << retval << "'" << endl;
}
return 0;
}
当我在我的机器上运行时,该程序的输出是:
The object @ 0017F938 said: 'My value is 1'
The object @ 0017F93C said: 'My value is 2'
The object @ 0017F940 said: 'My value is 3'
The object @ 0017F944 said: 'My value is 5'
The object @ 0017F948 said: 'My value is 8'
The object @ 0017F94C said: 'My value is 13'
答案 4 :(得分:0)
在这种情况下,您不能使用非静态成员函数。 基本上,glutMouseFunc预期的参数类型是
void (*)(int, int, int, int)
,而非静态成员函数的类型是
void (StartHand::*)(int, int, int, int)
第一个问题是类型并不真正匹配。 其次,为了能够调用该方法,回调必须知道你的方法属于哪个对象(即“this”指针)(这就是为什么类型首先不同的原因)。 第三,我认为你使用错误的语法来检索方法的指针。正确的语法应该是:&amp; StartHand :: MouseButton。
因此,您必须将该方法设置为静态或使用其他静态方法来知道用于调用MouseButton的StartHand指针。
答案 5 :(得分:0)
以下在c ++中用于定义c回调函数,例如当您只需要此类的单个实例时使用glut(glutDisplayFunc,glutKeyboardFunc,glutMouseFunc ...)时很有用:
MyClass * ptr_global_instance = NULL;
extern "C" void mouse_buttons_callback(int button, int state, int x, int y) {
// c function call which calls your c++ class method
ptr_global_instance->mouse_buttons_cb(button, state, x, y);
}
void MyClass::mouse_buttons_cb(int button, int state, int x, int y) {
// this is actual body of callback - ie. if (button == GLUT_LEFT_BUTTON) ...
// implemented as a c++ method
}
void MyClass::setup_glut(int argc, char** argv) { // largely boilerplate glut setup
glutInit(&argc, argv);
// ... the usual suspects go here like glutInitWindowSize(900, 800); ...
setupMouseButtonCallback(); // <-- custom linkage of c++ to cb
// ... other glut setup calls here
}
void MyClass::setupMouseButtonCallback() {
// c++ method which registers c function callback
::ptr_global_instance = this;
::glutMouseFunc(::mouse_buttons_callback);
}
在MyClass标题中,我们添加:
void mouse_buttons_cb(int button, int state, int x, int y);
void setupMouseButtonCallback();
这也可以使用相同的逻辑流来设置您的过剩 调用glutDisplayFunc(显示)