C++ class member function callback

2019-01-14 20:11发布

问题:

I have the following problem. I have a function from an external library (which cannot be modified) like this:

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

I would like to pass as the udf function above a function member of an existing class. Please look at the following code:

// 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!!
}

I cannot declare the classUDF member function as a static function, so the last line of the code above results in a compilation error!

回答1:

This is impossible to do - in c++, you must use either a free function, or a static member function, or (in c++11) a lambda without capture to get a function pointer.

GCC allows you to create nested function which could do what you want, but only in C. It uses so-called trampolines to do that (basically small pieces of dynamically generated code). It would be possible to use this feature, but only if you split some of the code calling externalFunction to a separate C module.

Another possibility would be generating code at runtime eg. using libjit.

So if you're fine with non-reenrant function, create a global/static variable which will point to this and use it in your static function.

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

Its really horrible code, but I'm afraid you're out of luck with such a bad design as your externalFunction.



回答2:

You can use Boost bind or TR1 bind (on recent compilers);;

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

Unfortunately, I lived in a pipe dream for the last 10 minutes. The only way forward is to call the target using some kind of a static wrapper function. The other answers have various neat (compiler-specific) tidbits on that, but here's the main trick:

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

Store a bound function in a variable like this:

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

(assuming C++0x support in compiler now. I'm sure Boost has a boost::function<> somewhere)

Vanilla C++ pointers-to-member-function

Without magic like that, you'd need pointer-to-memberfunction syntax:

See also live on http://ideone.com/Ld7It

Edit to clarify to the commenters, obviously this only works iff you have control over the definition of externalFunction. This is in direct response to the /broken/ snippet int the OP.

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);
}

C++98 idiomatic solution

// 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;
}


回答3:

Here is how I do this, when MyClass is a singleton:

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);
}