Обратный вызов функции-члена класса C

#c #class #function #callback #member

#c #класс #функция #обратный вызов #Участник

Вопрос:

У меня следующая проблема. У меня есть функция из внешней библиотеки (которая не может быть изменена), подобная этой:

 void externalFunction(int n, void udf(double*) );
  

Я хотел бы передать функцию udf над функцией-членом существующего класса. Пожалуйста, посмотрите на следующий код:

 // External function (tipically from an external library)
void externalFunction(int n, void udf(double*) )
{
     // do something
}

// User Defined Function (UDF)
void myUDF(double* a)
{
      // do something
}

// Class containing the User Defined Function (UDF)
class myClass
{
public:
    void classUDF(double* a)
    {
        // do something...
    };
};

int main()
{
    int     n=1;

    // The UDF to be supplied is myUDF
    externalFunction(n, myUDF);

    // The UDF is the classUDF member function of a myClass object
    myClass myClassObj;
    externalFunction(n, myClassObj.classUDF);   // ERROR!!
}
  

Я не могу объявить функцию-член classUDF как статическую функцию, поэтому последняя строка приведенного выше кода приводит к ошибке компиляции!

Комментарии:

1. Вы уверены, что ввели это правильно? Разделение externalFunction выглядит вымышленным.

2. @avakar: должно быть недействительным externalFunction(int n, void (*udf)(double*) ); для работы.

3. @jpalecek: Да, это то, что я подумал, хотя удивительно, что синтаксис OP действительно компилируется. Интересно, почему!

4. @KerrekSB: Указатель является неявным (вроде как int arr[] преобразуется в int* arr ). На самом деле я предпочитаю метод OP, более распространенный из них содержит ненужный шум.

5. проголосовавшие, не могли бы вы, пожалуйста, оставить комментарий! Я не вижу никаких причин для голосования против!

Ответ №1:

Это невозможно сделать — в c вы должны использовать либо свободную функцию, либо статическую функцию-член, либо (в c 11) лямбда-выражение без захвата, чтобы получить указатель на функцию.

GCC позволяет вам создавать вложенные функции, которые могли бы делать то, что вы хотите, но только на C. Для этого используются так называемые трамплины (в основном небольшие фрагменты динамически генерируемого кода). Можно было бы использовать эту функцию, но только в том случае, если вы разделите часть вызывающего кода externalFunction на отдельный модуль C.

Другой возможностью было бы генерировать код во время выполнения, например. с использованием libjit.

Итак, если вас устраивает невозвращаемая функция, создайте глобальную / статическую переменную, которая будет указывать на this , и используйте ее в вашей статической функции.

 class myClass
{
public:
    static myClass* callback_this;
    static void classUDF(double* a)
    {
        callback_this.realUDF(a);
    };
};
  

Это действительно ужасный код, но, боюсь, вам не повезло с таким плохим дизайном, как ваш externalFunction .

Комментарии:

1. Как можно с пользой найти экземпляр для вызова realUDF в лямбде без захвата?

2. @awoodland: Ты не можешь. Вот почему я сказал, что это невозможно.

Ответ №2:

Вы можете использовать Boost bind или TR1 bind (в последних компиляторах);;

 externalFunction(n, boost::bind(amp;myClass::classUDF, boost::ref(myClassObj)));
  

К сожалению, последние 10 минут я жил в несбыточной мечте. Единственный путь вперед — вызвать цель, используя какую-либо статическую функцию-оболочку. В других ответах есть различные аккуратные (зависящие от компилятора) детали по этому поводу, но вот главный трюк:

 void externalFunction(int n, void (*udf)(double*) )
{ double x; udf(amp;x); }

myClass myClassObj;
void wrapper(double* d) { myClassObj.classUDF(d); }

int main()
{
    externalFunction(1, amp;wrapper);
}
  

std::функция<>

Сохраните связанную функцию в переменной, подобной этой:

 std::function<void(double*)> stored = std::bind(amp;myClass::classUDF, boost::ref(myClassObj))
  

(предполагается, что C теперь поддерживает 0x в компиляторе. Я уверен, что у Boost где-то есть boost::function<>)

Ванильный C указывает на функцию-член

Без подобной магии вам понадобился бы синтаксис pointer-to-memberfunction:

Смотрите также прямой эфир на http://ideone.com/Ld7It

Редактировать чтобы пояснить комментаторам, очевидно, что это работает только, если у вас есть контроль над определением externalFunction . Это прямой ответ на / broken / snippet в OP.

 struct myClass
{
    void classUDF(double* a) { };
};

void externalFunction(int n, void (myClass::*udf)(double*) )
{
    myClass myClassObj;
    double x;
    (myClassObj.*udf)(amp;x); 
}

int main()
{
    externalFunction(1, amp;myClass::classUDF);
}
  

Идиоматическое решение C 98

 // mem_fun_ref example
#include <iostream>
#include <functional>
#include <vector>
#include <algorithm>
#include <string>

int main () 
{
    std::vector<std::string> numbers;

    // populate vector:
    numbers.push_back("one");
    numbers.push_back("two");
    numbers.push_back("three");
    numbers.push_back("four");
    numbers.push_back("five");

    std::vector <int> lengths (numbers.size());

    std::transform (numbers.begin(), numbers.end(), lengths.begin(), 
                std::mem_fun_ref(amp;std::string::length));

    for (int i=0; i<5; i  ) {
        std::cout << numbers[i] << " has " << lengths[i] << " letters.n";
    }
    return 0;
}
  

Комментарии:

1. Вы этого не делаете. Обновлен ответ. Однако TR1 и C 11 std::function<> решают проблему с указателем на функцию. Кроме того, C 98 std::mem_fun все время приходилось использовать с [ std::bind1 ](sthttp://www.cplusplus.com/reference/std/functional/bind1st/). Обновление ответа, чтобы показать использование

2. @GMan: отредактировал мой мозг: (Я не хотел просто отбрасывать остальную часть ответа. Это могло бы помочь кому-то понять проблемы — путем описания различных механизмов, которые были доступны в течение последнего десятилетия или около того.

3. @KerrekSB: извините за мой предыдущий ответ. Я пропустил подсказку. Замечание GMan сделало свое дело, чтобы разбудить меня

4. @sehe: Нет проблем. Это обычный запрос, и на него никогда не бывает простого ответа:-S

Ответ №3:

Вот как я это делаю, когда MyClass является синглтоном:

 void externalFunction(int n, void udf(double) );

class MyClass
{
public:
   static MyClass* m_this;
   MyClass(){ m_this = this; }
   static void mycallback(double* x){ m_this->myrealcallback(x); }
   void myrealcallback(double* x);
}

int main()
{
   MyClass myClass;
   externalFunction(0, MyClass::mycallback);
}