Introduction
In my previous article �Generic Observer Pattern and Events in C++�, we discussed classical �Observer Pattern� and its implementation in C++ using templates. An event class CppEvent
introduced in that article allowed us to bind together loosely coupled classes, by connecting one class containing CppEvent
to other class member functions. Member functions could be of any number and types of arguments, and no relations or special inheritance for model and view classes are required.
#include "CppEvent.h"
class MyView
{
public:
bool updateFun(int p)
{
cout << "on model updated with " << p << endl;
return true;
}
};
class MyModel
{
public:
CppEvent1<bool,int> event;
};
MyView* view = new MyView;
MyModel model;
CppEventHandler h = model.event.attach(view,& MyView::updateFun);
model.event.notify(1);
model.event.detach(h);
The only problem here is that we can�t delete our view without detaching it from the model first without risk of crashing our application on the next call of the event notification. It�s becoming annoying when we have many models created and deleted dynamically. To resolve this problem, we need a delegate which removes itself from the model when the view is deleted. To accomplish this task, we use Signal and Slot concept. This concept had been introduced in Trolltech Qt library and Boost C++ library.
Using Signal and Slots
To demonstrate how signals and slots work, we create a model class containing CppSignal
member and a view class containing CppSlot
.
#include "CppSlot.h"
class MyModel {
public:
void modelChanged()
{
m_signal.emit_sig(1);
}
CppSignal1<int,int> m_signal;
};
Note that we use CppSignal1
to specify signal with one parameter; for two and three parameters there are CppSignal2
and CppSignal3
. We could avoid this name multiplicity for standard C++ compiler, it�s done for portability with VC++ 6, which is still in use.
class MyView {
public:
MyView()
: m_slot(this,&MyView::onModelChanged)
{}
int onModelChanged(int i)
{
cout << "model changed:" << i <<
endl;
return 1;
}
CppSlot1<MyView,int,int> m_slot;
};
Note that slot member is initialized in the constructor of the view class. It�s initialized with pointer to the view class and member function. Now, to connect model to the view, we need to connect its signals with slots.
MyModel model;
MyView view;
model.m_signal.connect(&view.m_slot);
model.modelChanged();
Now we can create another view and connect it to the same model.
MyView1* view1 = new MyView1();
model.m_signal.connect(&view1->m_slot);
In order to track return values from view's callback functions, we can use collector function object. For example, an object counting return values will be:
class ResponseAccumulator
{
public:
typedef int return_type;
template <Typename iterator>int operator () (iterator begin,iterator end)
{
int sum = 0;
for(iterator it=begin; it!=end; ++it)
sum+=*it; return sum;
}
};
And when it is passed as a second parameter of emit_sig
method, it will return sum of view responses.
int res = model.m_signal.emit_sig(3,ResponseAccumulator());
cout << "sum of responses is " << res << endl;
Now we can delete one of the views and the sum of responses will change accordingly.
delete view1;
res = model.m_signal.emit_sig(3,ResponseAccumulator());
cout << "new sum of responses is " << res << endl;
Signals and Slots implementation
Implementation of CppSignal
and CppSlot
is very similar to CppEvent
in �Generic Observer Pattern and Events in C++�.