How do I setup/register a callback function, in C++, to call a function when there is data to be read from a queue?
Edit 1:
Using Neil's answer for a complete answer (in header file):
#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);
};
And the registering:
#include "File1.h"
MyListener::MyListener(MyQueue *q)
{
q->Register(this);
}
void MyListener::DataReady(class MyQueue *p)
{
Sleep(500);
}
Then the calls:
void __fastcall TForm1::Button1Click(TObject *Sender)
{
MyQueue *q = new MyQueue();
MyListe开发者_JS百科ner ml(q);
q->Add(1);
}
In outline, create a QueueListener base class:
class QueueListener {
public:
virtual void DataReady( class MyQueue & q ) = 0;
virtual ~QueueListener() {}
};
and a queue class (make this queue of integers as example:
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;
};
You derive the classes that want to be called back from QueueListener and implement the DataReady function. You then register instances of the derived class with your queue instance.
Have a look at Boost.Signals.
Example stolen from tutorial:
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();
I like the approach that boost.asio uses for callback. In ASIO they are referred to as handlers. Please excuse my c++0x, it is so much faster to write than 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 ); } );
}
The key points are the callback is a functor so the user isn't tied to a particular class hierarchy and the callbacks don't take any parameters. If you want parameters passed in, you bind them yourself. The exception is if the callback produces information not available when the callback was registered. An example could be the time when the item was added.
There is nothing stopping you from using this solution in c++98. You cannot use lamdbas, but boost::function
and boost::bind
are near identical to their c++0x counter parts.
Be aware that you'll have to manage object lifetimes carefully. That is the case with either Neil's or my solution.
精彩评论