C ++使用类方法作为函数指针类型

C++ Using Class Method as a Function Pointer Type

在C库中,有一个函数在等待函数指针,以便:

1
lasvm_kcache_t* lasvm_kcache_create(lasvm_kernel_t kernelfunc, void *closure)

其中lasvm_kernel_t定义为:

1
typedef double (*lasvm_kernel_t)(int i, int j, void* closure);

现在,如果我将类中定义的方法发送到lasvm kcache-create:

1
2
3
double cls_lasvm::kernel(int i, int j, void *kparam)
...
lasvm_kcache_t *kcache=lasvm_kcache_create(&kernel, NULL);

我得到:"无法将'double(cls_lasvm::)(int,int,void)'转换为'double()(int,int,void)'"

我该怎么办?


我假设closure参数是上下文"cookie",用于使用回调获取适当的上下文。这是回调函数的一个acomon习语,似乎是基于您提供的代码片段(但我不确定,因为除了您在这里发布的内容之外,我对kcache_create()一无所知)。

您可以使用该cookie向您正在处理的cls_lasvm实例传递一个指针,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
extern"C"
double
lasvm_kcache_create_callback( int i, int j, void* closure)
{
    // have to get a cls_lasvm pointer somehow, maybe the
    // void* clpsure is a context value that can hold the
    // this pointer - I don't know

    cls_lasvm* me = reinterpret_cast<cls_lasvm*>( closure);

    return me->kernel( i, j)

}


class cls_lasvm //...
{

    ...

    // the callback that's in the class doens't need kparam
    double cls_lasvm::kernel(int i, int j);

};

...

// called like so, assuming it's being called from a cls_lasvm
//  member function

lasvm_kcache_t *kcache=lasvm_kcache_create(&lasvm_kcache_create_callback, this);

如果我错误地认为闭包是上下文cookie,那么cls_lasvm类中的回调函数应该是静态的:

1
2
3
4
5
6
7
8
9
10
11
12
extern"C"
double
lasvm_kcache_create_callback( int i, int j, void* closure)
{
    // if there is no context provided (or needed) then
    // all you need is a static function in cls_lasvm

    return cls_lasvm::kernel( i, j, closure);
}

// the callback that's in the class needs to be static
static double cls_lasvm::kernel(int i, int j, void* closure);

注意C++中实现的C回调函数必须是EDOCX1×11。它可能在类中作为静态函数工作,因为类静态函数通常使用与C函数相同的调用约定。但是,这样做是一个等待发生的错误(见下面的注释),所以请不要使用extern"C"包装。

如果closure不是上下文cookie,并且由于某种原因,cls_lasvm::kernel()不能是静态的,那么您需要想出一种方法,将this指针存储在某个地方,并在lasvm_kcache_create_callback()函数中检索该指针,类似于我在第一个示例中所做的,只是指针必须来自您自己设计的某种机制。注意,这可能会使使用lasvm_kcache_create()不可重入和非线程安全。根据您的具体情况,这可能是一个问题,也可能不是问题。


如果它是一个不能修改代码的外部C库,那么就没有什么可以做的了。您将无法调用成员函数,因为它们需要this指针才能正常工作(以获取对象的属性)。我能想到的最简单的解决方法是使用第三个void*参数来传递this指针。在定义一个以上typedef之后,可以像这样定义结构:

1
2
3
4
5
6
7
8
9
typedef double (cls_lasvm::*lasvm_kernel_t_member)(int i, int j, void* closure);


struct MyParam
{
   A* pThis;
   lasvm_kernel_t_member pMemFun;
   void* kParam;
};

我还没有编译它,希望它有意义。

然后在类中定义一个从库接收调用的静态方法:

1
2
3
4
5
6
7
8
class cls_lasvm
{
  static double test(int i, int j, void *kparam)
  {
    MyParam* pParam = reinterpret_cast<MyParam*>(kparam);
    return (pParam->*pMemFun)(i,j,pParam->kParam);
  }
};

打电话时,您应使用以下内容:

1
2
3
4
5
6
7
cls_lasvm a;
MyParam param;
param.pThis = &a;
param.pMemFun = &cls_lasvm::kernel;
param.kParam = NULL;

lasvm_kcache_create(&cls_lasvm::test,&a);


每个C++成员函数都有一个隐式的、隐藏的、第一个参数EDCOX1(0)。

因此,方法double cls_lasvm::kernel(int i, int j, void* kparam)实际上是:double cls_lasvm::kernel(cls_lasvm* this, int i, int j, void* kparam),不适合/不可能将其用作函数指针参数。

要取得进展,请将方法转换为静态成员方法。这将删除this指针。你可能还有其他的问题要克服,但那是一个好的开始。