将合格的非静态成员函数作为函数指针传递

时间:2009-01-31 16:53:20

标签: c++

我在外部库中有一个函数,我无法使用以下签名进行更改:

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

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

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

registerResizeCallback(&Window::Resize);

所以我不确定如何解决这个问题。

3 个答案:

答案 0 :(得分:9)

作为Igor Oks indicates,你无法做到这一点。这个问题的其余部分并不是对你的问题的回答,而是讨论这样的事情应该如何与正确设计的回调API一起工作(看起来你正在使用的那个不是)。

大多数精心设计的回调接口允许您提供“void *”或其他一些方法来获取回调中的上下文。在C ++中使用它的一种常用方法是在void *上下文参数中传递一个对象指针,然后回调函数可以将其强制转换为对象指针并调用成员方法来完成实际工作。你正在使用的回调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 :(得分:6)

在C ++ FAQ Lite上查看"[33.2] How do I pass a pointer-to-member-function to a signal handler, X event callback, system call that starts a thread/task, etc?"

  

别。

     

因为成员函数在没有要调用它的对象的情况下没有意义,所以不能直接执行此操作

     

...

     

作为现有软件的补丁,使用顶级(非成员)函数作为包装器,它通过其他技术获取对象。

答案 2 :(得分:1)

扩展Michael Burr的建议,您必须了解非成员函数如何访问您正在修改的对象的实例。一种常见的方法是利用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 ++之间的桥梁,然后问题是如何获取对象将“回调”作为成员函数调用的实例。

在您的环境中可能还有其他原因导致上面的全局示例无效,因此您的任务是找出除了全局变量之外的其他机制允许您根据您的情况将对象传递回调。