Обратный вызов в C ++ для члена класса - PullRequest
6 голосов
/ 07 февраля 2011

У нас есть простая библиотека общения для наших клиентов.

Моя проблема: Как я могу сохранить указатель на метод из класса нашего клиента?

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 ]

7 голосов
/ 07 февраля 2011

Основная идея заключается в том, что вы определяете абстрактный класс Callback, который фактически передается вашему интерфейсу. Этот вызывает функцию, передающую единственный параметр int:

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

Этот класс позволяет ВАШЕЙ реализации быть свободной от знания кода, в который нужно перезвонить. Конечно, чтобы позвонить в класс, вам нужно создать экземпляр 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);
  }
};

Чтобы создать экземпляр обратного вызова из их класса, код должен выглядеть следующим образом. И вызов тоже прост:

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{ ... };
5 голосов
/ 07 февраля 2011

Основная идея состоит в том, чтобы скрыть точный тип объекта и функции (Object и Function в вашем коде) за виртуальным вызовом функции, и обернуть их в абстрактный интерфейс (это «стирание типа»).идиома).

Затем вы можете позволить своим клиентам получать данные о вашем типе "базового обратного вызова" через интерфейс шаблона.

Обучающее руководство см. в части 4. на этом сайте .Или посмотрите, как работают Boost.Function и Boost.Bind (они делают именно это, хотя и с немного более мощным интерфейсом)

3 голосов
/ 07 февраля 2011

Самый простой и гибкий способ - использовать 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.

Я также могу использовать лямбда-выражение, например:

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

Третий пример (и, вероятно, тот, который вам нужен) - это передача экземпляра класса, который определил функцию-оператор, например:

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 различных типа «ссылок на функции» (указатели на функции, лямбда-выражения и функторы).

0 голосов
/ 07 февраля 2011

Спасибо за ответы, но у меня все еще есть проблемы с реализацией (я обычно программирую на java или на C (микроконтроллер). Никогда не думал, что мне будет так сложно реализовать это на c ++))

Мне нравится использовать предложение Криса Бекке.

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 голосов
/ 07 февраля 2011

Код, приведенный ниже, как-то работает, но imho весь дизайн очень подозрительно.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();

}
...