2009-06-16 3 views
34

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

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

ответ

37

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

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

+0

Да. это решение сработало. Что смущает меня, то компилятор не показал ошибку int (A ::) (A *, int *, int *) 'не соответствует' int() (int, int *) ' – Methos

+0

Это было сделано, но, поставив (A: :), что означает, что функция является частью класса A, откуда отсюда подразумевается «этот» указатель. – GManNickG

+0

Мне просто интересно ... это указано в стандарте? Я просто взглянул на раздел о занятиях и не нашел этого. Тем не менее, очень интересно. Я просто не думаю, что каждый компилятор обязательно должен обрабатывать нестатические функции-члены таким образом. – Tom

1

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

Проще всего было бы сделать следующее:

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

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

, значит, вы не хотите, чтобы это было функцией члена? – Methos

+0

В этом случае да. ИМО большая простота, предоставляемая с помощью автономной функции, перевешивает отсутствие инкапсуляции. – PaulJWilliams

7

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

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

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

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

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

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

5

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

Thunking in Win32: Simplifying callbacks to non-static member functions

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

13

Вы также можете сделать это, если функция-член не является статическим, но это требует немного больше работы (см также Convert C++ function pointer to c function pointer):

#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.

+0

Прохладный! Я всегда хотел знать, как это сделать. – Jacko

+0

Что делать, если обратный вызов C имеет форму int __stdcall callback (int *, int *)? – Jacko

+0

@Jacko. Ммм ... это о вызывающем/вызывающем, ответственном за очистку стека, не так ли? Я не знаю ... Я все забыл о Windows. :-) –

Смежные вопросы