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

StackOverflow https://stackoverflow.com/questions/1000663

  •  05-07-2019
  •  | 
  •  

Вопрос

У меня есть библиотека 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*)’.

Мои вопросы:

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

    Решение

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

    Нестатические функции-члены класса A имеют неявный первый параметр типа class A * , который соответствует указателю this . Поэтому их можно зарегистрировать только в том случае, если подпись обратного вызова также имеет первый параметр типа 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 );
    

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

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

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

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

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

    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; }
    
    Лицензировано под: CC-BY-SA с атрибуция
    Не связан с StackOverflow
    scroll top