开发者

Callback in C++ to a class member

We have a simple communication library for our customers.

My problem is: How can I save a pointer to a method from a class of our customer?

Library.h is the header file with all the methods our开发者_如何学Python customer need to establish a communication.

library.cpp is our code. Somewhere here i have to save the pointers to the method of the callback function from our customer.

customer.cpp is an example how a customer uses our library.

library.h:

// This is the header file what our customer gets
class Library {
  public:
    template <class Object, class Function>
    void SetCallback(Object &obj, Function f);
};

library.cpp:

struct T_CUSTOMER {
    Object o;   // <- ???
    Function f; // <- ???
} customer;

void Library::SetCallback(Object &obj, Function f) {
    //Saving the method from our costumer
    customer.o = obj;   // <- ???
    customer.f = f;     // <- ???
}

void someFunction(void) {
    // here i want to call the method from the customer
    customer.o->customer.f(); //<- ???
}

customer.cpp:

class AnyCustomerClass {
    private:
        Library lib;

    public:
        AnyCustomerClass() {
            //< here the customer sets his method which I should call
            lib.SetCallback(this, &AnyCustomerClass::callback());
        }

        callback() {
            // do something
        }
}

Thanks for any help!


The basic idea is, you define an abstract Callback class, which actually gets passed to your interface. This one calls back to a function passing a single int parameter:

struct Callback {
  virtual ~Callback(){}
  virtual void operator()(int param)=0;
};

This class allows YOUR implementation to be free from knowledge of the code you need to call back into. Of course, to call into a class, you do need an instantiation of Callback that DOES have knowledge of its target. So you then also provide a templated child class that makes it easy for users of your Library to to bind a method in one of their classes to an instance of the generic Callback :-

template<class T>
class ClassCallback : public Callback {
  T* _classPtr;
  typedef void(T::*fncb)(int param);
  fncb _cbProc;
public:
  ClassCallback(T* classPtr,fncb cbProc):_classPtr(classPtr),_cbProc(cbProc){}
  virtual void operator()(int param){
    (_classPtr->*_cbProc)(param);
  }
};

To create an instance of the callback from their class, code would look like this. And the invocation is simple too:

struct CMyClass {
  Library* _theLibrary;
  CMyClass(Library* init):_theLibrary(init){
    Callback* pCB = new ClassCallback<CMyClass>(&myClass,&CMyClass::OnCb);
    _theLibrary->SetCallback(pCB);
  }
  void OnCb(int){
    // callback triggered
  }
  void Run(){
    _theLibrary->DoWork();
  }
};

In Summary: Library.h then would look like this. Define the abstract callback class, your library class, and the templated utility class that the customer uses to wrap their their class and its callback method with:

// This is the header file what our customer gets
struct Callback {... };
class Library {
  Callback* _pcb;
  public:
    void SetCallback(Callback* pcb){_pcb=pcb;}
    void DoWork(){
      int status=0;
      (*pcb)(status);
    }
    ~Library(){delete _pcb;}

};
template<class T> struct ClassCallback{ ... };


The basic idea is to hide the exact type of the object and function (Object and Function in your code) behind a virtual function call, and wrap both in an abstract interface (This is the 'type erasure' idiom).

You can then let your customers derive from your "basic callback" type via a template interface.

For a tutorial, see part 4. on this website. Or take a look at how Boost.Function and Boost.Bind work (they do exactly that, although with a slightly more powerful interface)


The easiest and most flexible way is to use std::function.

Suppose I have a function (but this could be a class as well), which needs to call another function passed to it. I define this function like this:

#include <functional>         // defines std::function
#include <iostream>

typedef std::function<bool(int)> FunctionPtr;

void myFunction (const FunctionPtr &functionPtr)
{
std::cout << "Before" << std::endl;
functionPtr(123);
std::cout << "After" << std::endl;
}

A first example on how to use this is using a global function (or static method), like this:

bool myFunPtr(int i)
   {
   std::cout << "FunPtr:" << i << std::endl;
   return true;
   }

int main()
{
myFunction (myFunPtr);
}

I just pass the function pointer to myFunction.

I can also use a lambda expression, like this:

int main()
{
myFunction ([](int i)->bool{std::cout << "Lambda:" << i << std::endl;return true;});
}

The third example (and which is probably what you want), is to pass a class instance that has defined the function-operator, like this:

class X
   {
   public:
      X(std::string s) : m_s(s) {}
      bool operator()(int i)
         {
         std::cout << m_s << i << std::endl;
         return true;
         } 
   private:
      std::string m_s;
   };

int main()
{
myFunction ([](int i)->bool{std::cout << "Lambda:" << i << std::endl;return true;});

myFunction (myFunPtr);

X x1("x1:");
myFunction (x1);

X x2("x2:");
myFunction (x2);
}

As you can see, using std::function, I can pass 3 different types of 'references to functions' (function pointers, lambda's and functors).


The code below seems to work somehow, but imho the whole design is very suspicious. http://codepad.org/9QDcMJAg

#include <stdio.h>

struct Library {
  template <class Object,class Function>
  void SetCallback(Object &obj, Function f);
};


struct Object {
  void Method( void );
};

typedef void (Object::*Function)(void);

struct T_CUSTOMER {
  Object o;  
  Function f;
};

T_CUSTOMER customer;

template <class Object,class Function>
void Library::SetCallback( Object &obj, Function f ) {
  customer.o = obj;
  customer.f = f;  
}

void someFunction(void) {
  (customer.o.*customer.f)();
}

struct AnyCustomerClass : Object {
  Library lib;

  AnyCustomerClass() {
    lib.SetCallback( *this, (Function)&AnyCustomerClass::callback );
  }

  void callback(void) {
    printf( "callback!\n" );
  }
};

int main( void ) {

  AnyCustomerClass a;

  someFunction();

}


Thanks for the answers, but i still have problems with the implementation (I normaly program in java or in C (microcontoller). Never thought it would be so complicated for me to realise this in c++))

I like to use the suggestion from Chris Becke.

library.h

// This is the header file what our customer gets
struct Callback {
  virtual void operator()(int param)=0;
};
class Library {
    Callback *cb;
  public:
    void SetCallback(Callback*);
};
template<class T>
class ClassCallback : public Callback {
  T* _classPtr;
  typedef void(T::*fncb)(int dwTime);
  fncb _cbProc;
public:
  ClassCallback(T* classPtr,fncb cbProc):_classPtr(classPtr),_cbProc(cbProc){}
  virtual void operator()(int param){
    (_classPtr->*_cbProc)(param);
  }
};

customer.cpp

class T_Customer{
private:
    Library lib;
    void OnCallback(int param){
          printf("Parameter: %i\n",param);
      }
    };
public:
    T_Customer(){
        lib.SetCallback(new ClassCallback<T_Customer>(this,&T_Customer::OnCallback));
    }
};

Library.cpp

void Library::SetCallback(Callback *callback){
    cb = callback;
}


void executeCallback(){
  if(cb)
    (*cb)();
}

int main(void){
    T_Customer customer;
    executeCallback();
    return 0;
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜