Как добавить / разработать функцию обратного вызова - PullRequest
2 голосов
/ 27 апреля 2010

Как мне настроить / зарегистрировать функцию обратного вызова в C ++, чтобы вызывать функцию, когда есть данные для чтения из очереди?

Редактировать 1:

Использование ответа Нейла для полного ответа (в заголовочном файле):

#include <vector.h>

class QueueListener {
   public:
       virtual void DataReady(class MyQueue *q) = 0;
       virtual ~QueueListener() {}
};

class MyQueue {
   public:
       void Add (int x) {
          theQueue.push_back(x);
          for (int i = 0; i < theCallBacks.size(); i++) {
             theCallBacks[i]->DataReady(this);
          }
       }

       void Register (QueueListener *ql) {
            theCallBacks.push_back(ql);
       }


   private:
       vector <QueueListener *> theCallBacks;
       vector <int> theQueue;
};



class MyListener : public QueueListener {
   public:
       virtual ~MyListener () {
          printf("MyListener destructor!");
       }
       MyListener(MyQueue *q);
       virtual void DataReady(class MyQueue *p);
};

И регистрация:

#include "File1.h"


MyListener::MyListener(MyQueue *q)
{
   q->Register(this);
}

void MyListener::DataReady(class MyQueue *p)
{
   Sleep(500);
}

Тогда звонки:

void __fastcall TForm1::Button1Click(TObject *Sender)
{
    MyQueue *q = new MyQueue();
    MyListener ml(q);

    q->Add(1);

}

Ответы [ 3 ]

3 голосов
/ 27 апреля 2010

Посмотрите на Boost.Signals.

Пример, украденный из учебника:

struct HelloWorld 
{
  void operator()() const 
  { 
    std::cout << "Hello, World!" << std::endl;
  } 
};

// ...

// Signal with no arguments and a void return value
boost::signal<void ()> sig;

// Connect a HelloWorld slot
HelloWorld hello;
sig.connect(hello);

// Call all of the slots
sig();
3 голосов
/ 27 апреля 2010

В общих чертах, создайте базовый класс QueueListener:

class QueueListener {
   public:
       virtual void DataReady( class MyQueue & q ) = 0;
       virtual ~QueueListener() {}
};

и класс очереди (создайте эту очередь из целых чисел в качестве примера:

class MyQueue {

   public:
      void Add( int x ) {
          theQueue.push_back( x );
          for ( int i = 0; i < theCallBacks.size(); i++ ) {
              theCallBacks[i]->DataReady( * this );
          }
      }

      void Register( QueueListener * ql ) {
          theCallBacks.push_back( ql );
      }

  private:

    vector <QueueListener *> theCallBacks;
    SomeQueueType <int> theQueue;

};

Вы получаете классы, которые хотите вызвать из QueueListener, и реализует функцию DataReady. Затем вы регистрируете экземпляры производного класса в своем экземпляре очереди.

2 голосов
/ 27 апреля 2010

Мне нравится подход, который boost.asio использует для обратного вызова. В ASIO они называются обработчиками. Прошу прощения, мой c ++ 0x, так писать гораздо быстрее, чем c ++ 98.

class MyQueue
{
   //...
   Register( const std::function<void()>& callback )
   {
      m_callbacks.push_back(callback);
   }

   Add( const int& i )
   {
      // ...

      for( const auto& callback: m_callbacks )
      {
         callback();
      }
   }

   std::vector<std::function<void()>> m_callbacks;
};

class SomeClass
{
public:
   void SomeQueueIsReady( MyQueue& )
   { /* do something with MyQueue */ }
};

void register_callback()
{
   SomeClass some;
   MyQueue queue;

   // using bind
   queue.Register( std::bind( &SomeClass::SomeQueueIsReady, &some, std::ref(queue) ) );

   // or using a lambda
   queue.Register( [&queue,&some](){ some.SomeQueueIsReady( queue ); } );
}

Ключевыми моментами являются то, что обратный вызов является функтором, поэтому пользователь не привязан к определенной иерархии классов и обратные вызовы не принимают никаких параметров. Если вы хотите передать параметры, вы должны связать их сами. Исключение составляют случаи, когда обратный вызов создает информацию, недоступную при регистрации обратного вызова. Примером может служить время, когда элемент был добавлен.

Ничто не мешает вам использовать это решение в c ++ 98. Вы не можете использовать lamdbas, но boost::function и boost::bind почти идентичны их счетным частям c ++ 0x.

Помните, что вам придется тщательно управлять временем жизни объекта. Так обстоит дело с Нейлом или моим решением.

...