Использование функции-члена класса C++ в качестве функции обратного вызова C

у меня есть библиотека 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. прежде всего, можно ли зарегистрировать функцию memeber класса C++, как я пытаюсь сделать, и если да, то как? (Я прочитал 32.8 в http://www.parashift.com/c++-faq-lite/mixing-c-and-cpp.html. Но, на мой взгляд, это не решает проблему)
  2. есть ли альтернативный / лучший способ решить эту проблему?

5 ответов


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

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


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


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

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

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

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

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

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


хорошо ...если вы находитесь на платформе win32, всегда есть неприятный способ...

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

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


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

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

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

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