C++ Using Class Method as a Function Pointer Type

后端 未结 3 491
庸人自扰
庸人自扰 2020-11-29 11:53

In a C lib, there is a function waiting a function pointer such that:

lasvm_kcache_t* lasvm_kcache_create(lasvm_kernel_t kernelfunc, void *closure)


        
相关标签:
3条回答
  • 2020-11-29 12:19

    I'm assuming that the closure argument is a context 'cookie' for the use of the callback to get appropriate context. This is a acomon idiom for callback functions, and seems to be what's going on based on the snippets you've provided (but I don't know for sure, as I don't know anything about kcache_create() except what you posted here).

    You can use that cookie to pass a pointer to the cls_lasvm instance you're dealing with like so:

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

    If I'm wrong about closure being a context cookie, then your callback function in the cls_lasvm class needs to be static:

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

    Note that a C callback function implemented in C++ must be extern "C". It may seem to work as a static function in a class because class-static functions often use the same calling convention as a C function. However, doing that is a bug waiting to happen (see comments below), so please don't - go through an extern "C" wrapper instead.

    If closure isn't a context cookie and for some reason cls_lasvm::kernel() can't be static then you need to come up with a way to stash a this pointer somewhere and retrieve that pointer in the lasvm_kcache_create_callback() function, similar to the way I did in my first example, except that pointer has to come dfrom some mechanism you devise yourself. Note that this will likely make using lasvm_kcache_create() non-reentrant and non-threadsafe. That may or may not be a problem depending on your specific circumstances.

    0 讨论(0)
  • 2020-11-29 12:23

    Every C++ member function has an implicit, hidden, first parameter, this.

    So the method double cls_lasvm::kernel(int i, int j, void* kparam) is really: double cls_lasvm::kernel(cls_lasvm* this, int i, int j, void* kparam), and it is inappropriate/impossible to use it as a function-pointer parameter.

    To make progress, convert your method to be a static-member-method. That will remove the this pointer. You may still have other issues to overcome, but that is a good start.

    0 讨论(0)
  • 2020-11-29 12:29

    If it is an external C library whose code you can not modify, then there is not much you can do about it. You'll not be able to call the member function as they require this pointer to work properly (to get the attributes of the object). The easiest workaround, I can think of is using third void* param to pass around this pointer. You can define struct like after defining one more typedef like:

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

    I haven't compiled it, I hope it makes sense.

    Then in your class define a static method which receives the call from library:

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

    While calling you should use something like:

    cls_lasvm a;
    MyParam param;
    param.pThis = &a;
    param.pMemFun = &cls_lasvm::kernel;
    param.kParam = NULL;
    
    lasvm_kcache_create(&cls_lasvm::test,&a);
    
    0 讨论(0)
提交回复
热议问题