2010-04-27 5 views
2

Comment configurer/enregistrer une fonction de rappel, en C++, pour appeler une fonction lorsqu'il y a des données à lire dans une file d'attente?Comment ajouter/concevoir la fonction de rappel

Edit 1:

En utilisant la réponse de Neil pour une réponse complète (dans le fichier d'en-tête):

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

Et l'enregistrement:

#include "File1.h" 


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

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

Ensuite, les appels:

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

    q->Add(1); 

} 

Répondre

3

Dans les grandes lignes, créer une classe de base QueueListener:

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

et une classe de file d'attente (faire cette file d'attente d'entiers comme par exemple:

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; 

}; 

Vous dérivez les classes qui veulent être appelées de QueueListener et implémentez la fonction DataReady. Vous enregistrez ensuite les instances de la classe dérivée avec votre instance de file d'attente.

+1

Cela semble un peu java-y ou au moins C++ 98. Je préfère les foncteurs et me lie. –

+0

Merci Neil - Je l'ai eu à travailler. J'ai dû changer le ceci à ceci dans la déclaration de DataReady sous MyQueue cependant. –

+0

@Caspin Première fois que j'ai été accusé d'écrire du code Java. Et pourquoi n'avez-vous pas produit une solution utilisant des foncteurs et liez-vous? –

3

Regardez Boost.Signals.

Exemple volé tutoriel:

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

J'aime l'approche que boost.asio utilisations pour le rappel. En ASIO, ils sont appelés gestionnaires. S'il vous plaît excusez mon C++ 0x, il est tellement plus rapide à écrire que 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); }); 
} 

Les points clés sont le rappel est un foncteur afin que l'utilisateur ne soit pas lié à une hiérarchie de classe particulière et les callbacks ne prennent pas de paramètres. Si vous voulez que les paramètres soient passés, vous les liez vous-même. L'exception est si le rappel produit des informations non disponibles lors de l'enregistrement du rappel. Un exemple pourrait être l'heure à laquelle l'élément a été ajouté.

Rien ne vous empêche d'utiliser cette solution dans C++ 98. Vous ne pouvez pas utiliser lamdbas, mais boost::function et boost::bind sont presque identiques à leurs parties de compteur C++ 0x. Sachez que vous devrez gérer avec soin les durées de vie des objets. C'est le cas de Neil ou de ma solution.