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

时间:2009-11-15 18:13:56

标签: c++ function-pointers

在C lib中,有一个等待函数指针的函数:

lasvm_kcache_t* lasvm_kcache_create(lasvm_kernel_t kernelfunc, void *closure)

其中lasvm_kernel_t定义为:

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

现在,如果我将类中定义的方法发送到lasvm_kcache_create:

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 ) ““

我该怎么办?

3 个答案:

答案 0 :(得分:5)

我假设closure参数是一个上下文'cookie',用于使用回调来获取适当的上下文。这是一个用于回调函数的acomon习语,似乎是根据你提供的片段进行的(但我不确定,因为我对kcache_create()一无所知,除了你发布在这里)。

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

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类中的回调函数需要是静态的:

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回调函数必须extern "C"。它似乎在类中作为静态函数工作,因为类静态函数通常使用与C函数相同的调用约定。但是,这样做是一个等待发生的错误(请参阅下面的评论),所以请不要 - 转而使用extern "C"包装。

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

答案 1 :(得分:0)

每个C ++成员函数都有一个隐含的隐藏的第一个参数this

所以方法double cls_lasvm::kernel(int i, int j, void* kparam)确实是: double cls_lasvm::kernel(cls_lasvm* this, int i, int j, void* kparam),将它用作函数指针参数是不合适/不可能的。

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

答案 2 :(得分:0)

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

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;
};

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

然后在你的类中定义一个静态方法,它接收来自库的调用:

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);
  }
};

在打电话时你应该使用类似的东西:

cls_lasvm a;
MyParam param;
param.pThis = &a;
param.pMemFun = &cls_lasvm::kernel;
param.kParam = NULL;

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