C ++ Использование метода класса в качестве типа указателя на функцию - PullRequest
3 голосов
/ 15 ноября 2009

В 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 ]

5 голосов
/ 15 ноября 2009

Я предполагаю, что аргумент 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() без повторного входа и без поточного. Это может или не может быть проблемой в зависимости от ваших конкретных обстоятельств.

0 голосов
/ 15 ноября 2009

Если это внешняя библиотека C, код которой вы не можете изменить, то с этим ничего не поделаешь. Вы не сможете вызывать функцию-член, поскольку для правильной работы требуется указатель this (чтобы получить атрибуты объекта). Самый простой обходной путь, который я могу придумать, - использовать третий void* параметр для обхода this указателя. Вы можете определить struct like после определения еще одного typedef, например:

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);
0 голосов
/ 15 ноября 2009

Каждая функция-член 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), и нецелесообразно / невозможно использовать его в качестве параметра-указателя функции.

Чтобы добиться прогресса, преобразуйте ваш метод в метод static-member. Это удалит указатель this. Возможно, вам еще придется преодолеть другие проблемы, но это хорошее начало.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...