Обратный вызов функции-члена класса 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 как статическую функцию, поэтому последняя строка приведенного выше кода приводит к ошибке компиляции!


person alberto.cuoci    schedule 10.11.2011    source источник
comment
Вы уверены, что правильно написали? Декларация externalFunction выглядит фиктивной.   -  person Kerrek SB    schedule 10.11.2011
comment
@avakar: для работы должно быть void externalFunction(int n, void (*udf)(double*) );.   -  person jpalecek    schedule 10.11.2011
comment
@jpalecek: Да, я так и думал, хотя на удивление синтаксис OP действительно компилируется. Интересно, почему!   -  person Kerrek SB    schedule 10.11.2011
comment
@KerrekSB: указатель неявный (вроде как int arr[] превращается в int* arr). На самом деле я предпочитаю метод OP, более распространенный из них содержит ненужный шум.   -  person GManNickG    schedule 10.11.2011
comment
проголосовавшие, не могли бы вы оставить комментарий! Не вижу причин для отрицательного голосования!   -  person Yousf    schedule 10.11.2011


Ответы (3)


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

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

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

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

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

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

person jpalecek    schedule 10.11.2011
comment
Как найти экземпляр для вызова realUDF в лямбде без захвата? - person Flexo; 10.11.2011
comment
@awoodland: Вы не можете. Вот почему я сказал, что это невозможно. - person jpalecek; 10.11.2011

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

<удар>

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

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

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

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

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

станд::функция‹>

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

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

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

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

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

Смотрите также прямую трансляцию на http://ideone.com/Ld7It.

Изменить, чтобы прояснить для комментаторов, очевидно, что это работает только если вы контролируете определение externalFunction. Это прямой ответ на фрагмент /broken/ в ОП.

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

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

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

Идиоматическое решение С++ 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(&std::string::length));

    for (int i=0; i<5; i++) {
        std::cout << numbers[i] << " has " << lengths[i] << " letters.\n";
    }
    return 0;
}
person sehe    schedule 10.11.2011
comment
Вы не знаете. Обновил ответ. Однако TR1 и C++11 std::function‹› решают проблему указателя на функцию. Кроме того, в C++98 постоянно использовался std::mem_fun. с [std::bind1](sthttp://www.cplusplus.com/reference/std/functional/bind1st/). Обновление ответа, чтобы показать использование - person sehe; 10.11.2011
comment
@GMan: отредактировал мое повреждение мозга :( Я не хотел просто отбрасывать остальную часть ответа. Это может помочь кому-то понять проблемы - путем описания различных механизмов, которые были доступны в течение последнего десятилетия или около того. - person sehe; 10.11.2011
comment
@KerrekSB: извините за мой предыдущий ответ. Я пропустил подсказку. Замечание GMan заставило меня проснуться. - person sehe; 10.11.2011
comment
@sehe: Нет проблем. Это распространенный запрос, и на него никогда не бывает простого ответа :-S - person Kerrek SB; 10.11.2011

Вот как я это делаю, когда 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);
}
person Yousf    schedule 10.11.2011