56

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?
sharptooth
  • 159,303
  • 82
  • 478
  • 911
Methos
  • 10,908
  • 11
  • 41
  • 47

6 Answers6

47

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.

sharptooth
  • 159,303
  • 82
  • 478
  • 911
  • 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 Jun 16 '09 at 10:31
  • 1
    It did, but by putting (A::) which means that function is part of class A, which from there implies the 'this' pointer. – GManNickG Jun 16 '09 at 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 Jun 16 '09 at 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 Jun 16 '09 at 11:00
  • 2
    @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 there – Johannes Schaub - litb Jun 16 '09 at 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. 'pronouns' m. Dec 18 '16 at 06:35
36

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) {                    
      return 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.

Anne van Rossum
  • 2,643
  • 1
  • 24
  • 37
6

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.

Rapptz
  • 19,461
  • 4
  • 67
  • 86
Raoul Supercopter
  • 4,985
  • 1
  • 31
  • 36
  • What's a 'method' in C++? That word doesn't ever appear one single time in C++ standard. – Aconcagua Apr 13 '19 at 12:38
  • @Aconcagua, I would imagine you know, but here is an answer to your question: https://stackoverflow.com/questions/8596461/in-c-what-is-the-difference-between-a-method-and-a-function#answer-8596481 – Alexis Wilke Apr 30 '19 at 18:07
  • A function member ("method") is definitely a function. The fact that there is (indeed) an additional parameter does not make it a non-function object. – Alexis Wilke Apr 30 '19 at 18:09
  • @AlexisWilke Much more important are the first two ***comments*** to the referred answer. Additionally second paragraph ("interchangability") would imply "function != function". It might look like splitting hairs at a first glance, but I had to learn it the hard way (slight misunderstandings leading to heavy bugs) how important clear definitions are. So deducing two important rules: 1. Don't use terminology that isn't clearly defined! 2. Don't use new definitions in parallel to existing ones. – Aconcagua May 01 '19 at 06:37
  • In given case, even both rules are violated, as there's already 'member function' and 'method isn't even clearly defined in C++. Not software now, but still an [example](https://en.wikipedia.org/wiki/Tenerife_airport_disaster#Communication_misunderstandings) what might happen if terminology is not clear (enough). Side note: terminology was standardised afterwards with clearly distinguishable wording... – Aconcagua May 01 '19 at 06:39
  • 1
    In `a->(j, k);`, did you miss typing the `e`? – Alexis Wilke May 02 '19 at 03:55
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.

TimW
  • 8,075
  • 1
  • 27
  • 32
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; }
PaulJWilliams
  • 18,187
  • 3
  • 48
  • 78
1

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

} // ()
cibercitizen1
  • 19,104
  • 14
  • 65
  • 90