2009-01-31 86 views
4

我有一个外部库中的函数,我不能用下面的签名更改:传递一个合格的非静态成员函数的函数指针

void registerResizeCallback(void (*)(int, int)) 

我想在成员函数传递作为回调,因为我的回调需要修改实例变量。

显然,这是不可能用一个简单的:

registerResizeCallback(&Window::Resize); 

,所以我真的不知道如何解决这个问题。

+1

在C中没有什么是不可能的。你的问题引起了我的兴趣,所以我写了一个关于解决它的黑客方式的博客:http://nothingintoinsight.blogspot.com/2009/02/how-to-hack-closures-in-your-c-code-or。 html – user51568 2009-02-01 20:14:43

回答

9

由于Igor Oks indicates,你不能这样做。这个问题的其余部分不是解决您的问题的答案,而是讨论如何使用正确设计的回调API(看起来您使用的不是)。

大多数精心设计的回调接口允许您提供“void *”或其他方式来获取回调中的上下文。在C++中使用这种方法的一个常见方法是在上下文参数中传递一个对象指针,然后回调函数可以将它转换回对象指针并调用成员方法来完成真正的工作。你使用的回调API太糟糕了,不能提供上下文数据。

严格地说,回调必须是extern "C",但使用静态成员方法进行回调很常见,我认为在实践中从来没有问题。 (这是假定回调API是一个C接口,这是迄今为止最常见的)。

一个例子:

// callback API declaration's 

extern "C" { 
    typedef unsigned int callback_handle_t; 

    typedef void (*callback_fcn_t)(void* context, int data1, int data2); 

    callback_handle_t RegisterCallback(callback_fcn_t, void* context); 
    void UnregisterCallback(callback_handle_t); 
} 

// ---------------------------------- 

// prototype for wrapper function that will receive the callback and 
// transform it into a method call 

extern "C" 
static void doWorkWrapper(void* context, int data1, int data2); 


// the class that does the real work 

class worker { 
public: 
    worker() { 
     hCallback = RegisterCallback(doWorkWrapper, this); 
    } 

    ~worker() { 
     UnregisterCallback(hCallback); 
    } 

    void doWork(int data1, int data2) { 
     // ... 
    }; 

private: 
    callback_handle_t hCallback; 
}; 

// the wrapper that transforms the callback into a method call 
extern "C" 
static void doWorkWrapper(void* context, int data1, int data2) 
{ 
    worker* pWorker = static_cast<worker*>(context); 

    pWorker->doWork(data1, data2); 
} 
1

扩大迈克尔伯尔的建议,你将不得不找出一个非成员函数如何获得对正在修改的对象的实例访问。一个常用的方法是利用静态全局范围在C:

// Top of your .c module: 
static Window *gMyWindow; 

// The declaration 
extern "C" { 
    void* my_callback(int, int); 
} 

// Later, set it just before handing off the callback 
void somefunc() { 
    ... 
    gMyWindow = &windowObjectRef; 
    registerResizeCallback(my_callback); 
    windowObjectRef.SomeOtherWindowCallCausingCallbackInvoke(); 
    ... 
} 

// The callback in the same .c module as the global 
void my_callback(int x, int y) { 
    Window *object = gMyWindow; 
    object->Resize(x, y); 
} 

我还没有编译/运行上面的代码,所以有可能在细节上的调整,但希望这个概念是明确的:回调必须是C和C++之间的桥梁,然后问题是如何让对象“进入”回调成为成员函数调用的实例。

在您的环境中可能还有其他原因,为什么上面的全局示例不起作用,那么您的任务是找出除了全局变量之外的其他机制将允许您根据您的情况将对象传入回调。