Using a C++ class member function as a C callback function

旧街凉风 提交于 2019-11-26 08:13:19

You can do that if the member function is static.

Non-static member functions of class A have an implicit first parameter of type class A* which corresponds to this pointer. That's why you could only register them if the signature of the callback also had the first parameter of class A* type.

Anne van Rossum

You can also do this if the member function is not static, but it requires a bit more work (see also 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;
}

This example is complete in the sense that it compiles:

g++ test.cpp -std=c++11 -o test

You will need the c++11 flag. In the code you see that register_with_library(func) is called, where func is a static function dynamically bound to the member function e.

Raoul Supercopter

The problem is that method != function. The compiler will transform your method to something like that:

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

So, it's sure you can't pass it, because the class instance can't be passed as argument. One way to work around is to make the method as static, this way it would have the good type. But it won't any class instance, and access to non-static class members.

The other way is to declare a function with a static Pointer to a A initialised the first time. The function only redirect the call to the class :

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

Then you can register the callback function.

Well ...if you are on a win32 platform there is always the nasty Thunking way ...

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

It is a solution but I don't recommend using it.
It has a good explanation and it is nice to know it exists.

The problem with using a member function is that it needs an object on which to act - and C doesnt know about objects.

The easiest way would be to do the following:

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

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

In this solution, we have a template class with the static method to be given to the "c function" as a callback. This class holds a "ordinary" object ( with a member function named callback() which will be finally called).

Once your class (here, A) is defined, it can be easily used:

int main() {

  Holder<A> o ( A(23, 23) );

  std::cout << o().getN() << "\n";

  callACFunctionPtr( fun );

  callACFunctionPtr( o.callback );

} // ()

Complete example:

#include <iostream>

// ----------------------------------------------------------
// library class: Holder
// ----------------------------------------------------------
template< typename HeldObjectType >
class Holder {
public:
  static inline HeldObjectType object;

  static void callback( ) {
    object.callback();
  } // ()

  HeldObjectType &  operator() ( ) {
    return object;
  }

  Holder( HeldObjectType && obj )
  {
    object = obj;
  }

  Holder() = delete;

}; // class

// ----------------------------------------------------------
// "old" C function receivin a ptr to function as a callback
// ----------------------------------------------------------
using Callback = void (*) (void);

// ..........................................................
// ..........................................................
void callACFunctionPtr( Callback f ) {
  f();
} // ()

// ----------------------------------------------------------
// ----------------------------------------------------------
void fun() {
  std::cout << "I'm fun\n";
} // 

// ----------------------------------------------------------
// 
// Common class where we want to write the
// callback to be called from callACFunctionPtr.
// Name this function: callback
// 
// ----------------------------------------------------------
class A {
private:
  int n;

public:

  A(  ) : n( 0 ) { }

  A( int a, int b ) : n( a+b ) { }

  void callback( ) {
    std::cout << "A's callback(): " << n << "\n";
  }

  int getN() {
    return n;
  }

}; // class

// ----------------------------------------------------------
// ----------------------------------------------------------
int main() {

  Holder<A> o ( A(23, 23) );

  std::cout << o().getN() << "\n";

  callACFunctionPtr( fun );

  callACFunctionPtr( o.callback );

} // ()
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!