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

Go To StackoverFlow.com

39

I have a C library that needs a callback function to be registered to customize some processing. Type of the callback function is int a(int *, int *).

I am writing C++ code similar to the following and try to register a C++ class function as the callback function:

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() 
{

}

The compiler throws following error:

In constructor 'A::A()',
error:
 argument of type ‘int (A::)(int*, int*)’ does not match ‘int (*)(int*, int*)’.

My questions:

  1. First of all is it possible to register a C++ class memeber function like I am trying to do and if so how? (I read 32.8 at http://www.parashift.com/c++-faq-lite/mixing-c-and-cpp.html. But in my opinion it does not solve the problem)
  2. Is there a alternate/better way to tackle this?
2009-06-16 10:24
by Methos


40

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.

2009-06-16 10:26
by sharptooth
yes. that solution worked. What confuses me is compiler did not show error int (A::)(A , int, int)’ does not match ‘int ()(int, int) - Methos 2009-06-16 10:31
It did, but by putting (A::) which means that function is part of class A, which from there implies the 'this' pointer - GManNickG 2009-06-16 10:37
I'm just curious... is this specified in the standard? I just glanced at the section on classes and didn't find this. Nevertheless, very interesting. I just wouldn't think that every compiler necessarily has to handle non-static member functions in this way - Tom 2009-06-16 10:59
@Methos, saying that member functions have an implicit first parameter doesn't mean that parameter really exist. It means that conceptually, it's there - Johannes Schaub - litb 2009-06-16 11:00
@Tom, the standard calls it "implicit object parameter", and it's of type A& for non-const member functions, and A const& for const member functions, A volatile& for volatile... and so on. It's a reference, while "this" is a pointer - mostly because of history. The object that the member function is called on is called "implied object argument". The implicit object parameter is treated as a hidden first parameter for purpose of overload resolution - but this all is only conceptual, nothing that really has to be ther - Johannes Schaub - litb 2009-06-16 11:02
"That's why you could only register them if the signature of the callback also had the first parameter of class A* type" --- doing so would be undefined behaviour and would not work for all kinds of member functions - n.m. 2016-12-18 06:35


14

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.

2015-04-23 07:52
by Anne van Rossum
Cool! I've always wanted to know how to do this - Jacko 2015-11-01 14:58
What if the C callback has is of the form int __stdcall callback(int, int) - Jacko 2015-11-01 15:53
@Jacko. Mmm... that's about callee/caller responsible for stack cleanup isn't it? I don't know... I forgot everything about Windows. :- - Anne van Rossum 2015-11-01 18:55
@Jacko: static Ret __stdcall callback(Arg arg - Martin.Martinsson 2016-08-14 17:15
How would one do this to be threadsafe? I've posted the question here: http://stackoverflow.com/questions/41198854/using-a-c-class-member-function-as-a-c-callback-function-thread-safe-versio - Victor.dMdB 2016-12-17 13:57


7

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.

2009-06-16 10:33
by Raoul Supercopter


5

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.

2009-06-16 11:09
by TimW


1

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; }
2009-06-16 10:29
by PaulJWilliams
so you mean do not make it a member function - Methos 2009-06-16 10:32
In this case, yes. IMO the greater simplicity afforded by using a standalone function outweighs the lack of encapsulation involved - PaulJWilliams 2009-06-16 10:34
Ads