androidFan
androidFan

Reputation: 621

C++ callback for non-static function between different classes

I have a Class A which I intend to put in a shared library as it interacts with the device drivers.

I have a Class B and may be C,D,E... in future which will use the class A using the shared library.

I want a capability of setting a callback function in Class A so that when a specific event occurs a non-static members function of class B,C,D,E ... should be called by Class A.

I searched on google for callback function in C++ but found that non-static member functions are not supported by C-style definition of callbacks.

Can it be done using function pointers ?

Kindly give some suggestions for callbacks in C++ which do not violate the OOPS concepts.

I also came around a library called 'Boost' which offers similar functionality but I want to avoid the overhead of the extra library if possible. Is Boost recommended for callback function ?

EDIT : The B,C,D,E will not share any hierarchy and they will be completely independent classes. But all of them would have object of class A. And class A would also have a public function to set the callback function.

Upvotes: 0

Views: 2625

Answers (2)

Xeo
Xeo

Reputation: 131907

One option, if you really really want to avoid the nearly unimportant overhead of a polymorphic function wrapper, is to make those functions static and have them take a "user data" void* parameter, pointing to an appropriate instance of the class the function is a member of. Inside the static function, you then cast back to the appropriate type:

#include <iostream>

struct A{
  typedef void (*callback_type)(void*, int);
  callback_type callback;
  void* user_data;
  void set_callback(callback_type cb, void* ud){
     callback = cb; user_data = ud;
  }
  void invoke(){ callback(user_data, 42); }
};

struct B{
  static void cb_foo(void* vself, int data){
    B* self = static_cast<B*>(vself);
    self->foo(data);
  }
  void foo(int data){ std::cout << data * 2 << "\n"; }
};

struct C{
  static void cb_bar(void* vself, int data){
    C* self = static_cast<C*>(vself);
    self->bar(data);
  }
  void bar(int data){ std::cout << data / 2 << "\n"; }
};

int main(){
  A a;
  B b;
  a.set_callback(&B::cb_foo, &b);
  a.invoke();
  C c;
  a.set_callback(&C::cb_bar, &c);
  a.invoke();
}

Live example on Ideone.

I personally would recommend using std::function, though, since the above is severly limited in what can be accepted as a callback. std::function is a polymorphic function wrapper, meaning that it can take normal function pointers, member function pointers and even functors (function objects) and invoke them all in the same manner. Together with std::bind, which allows you to bind parameters to a function, you can make easy callbacks to member functions. Boost offers them too (Boost.Function, Boost.Bind).

#include <iostream>
#include <functional> // C++11
//#include <boost/function.hpp>
//#include <boost/bind.hpp>

struct A{
  std::function<void(int)> callback;
  void invoke(){ callback(42); }
};

struct B{
  void foo(int data){ std::cout << data * 2 << "\n"; }
};

struct C{
  void bar(int data){ std::cout << data / 2 << "\n"; }
};

int main(){
  using namespace std::placeholders; // namespace for argument placeholders for std::bind
                                     // not needed for Boost.Bind
  A a;
  B b;
  a.callback = std::bind(&B::foo, &b, _1);
  a.invoke();
  C c;
  a.callback = std::bind(&C::bar, &c, _1);
  a.invoke();
};

Live example on Ideone.

Basically std::bind does automatically what you had to do manually in the first version, it saves the object pointer and invokes the member function on it. It doesn't do this through a void* pointer, however, and instead std::bind returns a different binder type for every different object pointer. That's why you need std::function, since it doesn't care what you pass it.

Upvotes: 4

Omaha
Omaha

Reputation: 2292

Assuming that {B, C, D, E} share some hierarchy so you do not need to write a new version for each class, make the callback function static but add an additional parameter that is a reference to the {B, C, D, E} instance that is involved in the callback. That way, once you are within the context of the function, you will be able to invoke nonstatic functions/operations on the relevant object.

If the class hierarchies for {B, C, D, E} or anything that comes up in the future are not the same and you cannot find a common starting point, you will probably need to resort to something more generic like a void pointer, though that makes it very difficult to know what functionality can be invoked on the object.

Upvotes: 0

Related Questions