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

У меня есть библиотека C, для регистрации которой требуется функция обратного вызова, чтобы настроить некоторую обработку. Тип функции обратного вызова: int a(int *, int *).

Я пишу код C ++, подобный следующему, и пытаюсь зарегистрировать функцию класса C ++ в качестве функции обратного вызова:

class A {
  public:
   A();
   ~A();
   int e(int *k, int *j);
};

A::A()
{
   register_with_library(e)
}

int
A::e(int *k, int *e)
{
  return 0;
}

A::~A() 
{

}

Компилятор выдает следующую ошибку:

In constructor 'A::A()',
error:
 argument of type ‘int (A::)(int*, int*)’ does not match ‘int (*)(int*, int*)’.

Мои вопросы:

  1. Прежде всего, возможно ли зарегистрировать функцию члена класса C ++, как я пытаюсь сделать, и если да, то как? (Я прочитал 32,8 на http://www.parashift.com/c++-faq-lite/mixing-c-and-cpp.html. Но, на мой взгляд, это не решает проблему)
  2. Есть ли альтернативный / лучший способ справиться с этим?

Ответы [ 5 ]

41 голосов
/ 16 июня 2009

Вы можете сделать это, если функция-член является статической.

Нестатические функции-члены класса A имеют неявный первый параметр типа class A*, который соответствует этому указателю. Поэтому их можно зарегистрировать только в том случае, если подпись обратного вызова также имеет первый параметр типа class A*.

16 голосов
/ 23 апреля 2015

Вы также можете сделать это, если функция-член не является статической, но требует немного больше работы (см. Также Преобразование указателя функции C ++ в указатель функции c ):

#include <stdio.h>
#include <functional>

template <typename T>
struct Callback;

template <typename Ret, typename... Params>
struct Callback<Ret(Params...)> {
   template <typename... Args> 
   static Ret callback(Args... args) {                    
      func(args...);  
   }
   static std::function<Ret(Params...)> func; 
};

template <typename Ret, typename... Params>
std::function<Ret(Params...)> Callback<Ret(Params...)>::func;

void register_with_library(int (*func)(int *k, int *e)) {
   int x = 0, y = 1;
   int o = func(&x, &y);
   printf("Value: %i\n", o);
}

class A {
   public:
      A();
      ~A();
      int e(int *k, int *j);
};

typedef int (*callback_t)(int*,int*);

A::A() {
   Callback<int(int*,int*)>::func = std::bind(&A::e, this, std::placeholders::_1, std::placeholders::_2);
   callback_t func = static_cast<callback_t>(Callback<int(int*,int*)>::callback);      
   register_with_library(func);      
}

int A::e(int *k, int *j) {
   return *k - *j;
}

A::~A() { }

int main() {
   A a;
}

Этот пример завершен в том смысле, что он компилируется:

g++ test.cpp -std=c++11 -o test

Вам понадобится флаг c++11. В коде вы видите, что вызывается register_with_library(func), где func - статическая функция, динамически связанная с функцией-членом e.

6 голосов
/ 16 июня 2009

Проблема в том, что метод! = Функция. Компилятор преобразует ваш метод в нечто подобное:

int e( A *this, int *k, int *j );

Итак, вы точно не можете передать его, потому что экземпляр класса не может быть передан в качестве аргумента. Один из способов обойти это сделать метод статичным, чтобы он имел хороший тип. Но это будет не любой экземпляр класса, а доступ к нестатическим членам класса.

Другой способ - объявить функцию со статическим указателем на A, инициализированную в первый раз. Функция только перенаправляет вызов в класс:

int callback( int *j, int *k )
{
    static A  *obj = new A();
    a->(j, k);
}

Затем вы можете зарегистрировать функцию обратного вызова.

5 голосов
/ 16 июня 2009

Ну ... если вы на платформе win32, всегда есть отвратительный способ Thunking ...

Thunking в Win32: Упрощение обратных вызовов для нестатических функций-членов

Это решение, но я не рекомендую его использовать.
У этого есть хорошее объяснение, и приятно знать, что оно существует.

1 голос
/ 16 июня 2009

Проблема с использованием функции-члена заключается в том, что ей нужен объект, с которым нужно действовать, а Си не знает об объектах.

Самый простой способ - сделать следующее:

//In a header file:
extern "C" int e(int * k, int * e);

//In your implementation: 
int e(int * k, int * e) { return 0; }
...