用C ++回调给类成员

时间:2011-02-07 08:36:20

标签: c++ function methods callback

我们为客户提供了一个简单的沟通库。

我的问题是:如何从我们客户的类中保存指向方法的指针?

Library.h是头文件,包含客户建立通信所需的所有方法。

library.cpp是我们的代码。在这里,我必须从我们的客户那里保存指向回调函数方法的指针。

customer.cpp是客户如何使用我们的库的示例。

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
        }
}

感谢您的帮助!

5 个答案:

答案 0 :(得分:7)

基本思想是,你定义一个抽象的Callback类,它实际上被传递给你的接口。这个回调函数传递一个int参数:

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

此类允许您的实现不需要您需要回调的代码的知识。当然,要调用一个类,你需要一个回调的实例,它已知道它的目标。因此,您还提供了一个模板化的子类,使您的库用户可以轻松地将其中一个类中的方法绑定到通用回调的实例: -

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

要从类中创建回调实例,代码将如下所示。调用也很简单:

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

总结:Library.h就是这样的。定义抽象回调类,库类以及客户用来包装其类及其回调方法的模板化实用程序类:

// 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{ ... };

答案 1 :(得分:5)

基本思想是在虚函数调用后隐藏对象和函数的确切类型(代码中的ObjectFunction),并将它们包装在抽象接口中(这是'类型擦除'成语)。

然后,您可以通过模板界面让您的客户从“基本回调”类型中获得。

有关教程,请参阅this website上的第4部分。或者看看Boost.FunctionBoost.Bind是如何工作的(他们确实如此,尽管界面稍微强大一些)

答案 2 :(得分:3)

最简单,最灵活的方法是使用std :: function。

假设我有一个函数(但也可以是一个类),它需要调用传递给它的另一个函数。我这样定义了这个函数:

#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;
}

关于如何使用它的第一个例子是使用全局函数(或静态方法),如下所示:

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

int main()
{
myFunction (myFunPtr);
}

我只是将函数指针传递给myFunction。

我也可以使用lambda表达式,如下所示:

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

第三个例子(可能是你想要的)是传递一个定义了function-operator的类实例,如下所示:

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

正如你所看到的,使用std :: function,我可以传递3种不同类型的'函数引用'(函数指针,lambda和functor)。

答案 3 :(得分:0)

下面的代码似乎以某种方式工作,但是整个设计非常可疑。 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();

}

答案 4 :(得分:0)

感谢您的回答,但我仍然遇到了实施问题(我在java或C(微控制器)中正常编程。从来没有想过在c ++中实现这一点会很复杂))

我喜欢使用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;
}