Click here to Skip to main content
15,883,705 members
Articles / Programming Languages / C++
Article

Implementation of Delegates in C++ using Signal and Slot pattern

Rate me:
Please Sign up or sign in to vote.
3.73/5 (7 votes)
12 Sep 20042 min read 48.9K   770   31   2
The article describes an efficient way to implement delegates in C++ using Signal and Slot pattern.

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"

// declare view class
class MyView 
{
public:
 bool updateFun(int p)
 {
   cout << "on model updated with " << p << endl;
   return true;
 }
};

// declare model class containing event
class MyModel
{
public:
 
CppEvent1<bool,int> event;   
};

// create viewers 
MyView* view = new MyView; 
// create model 
MyModel model; 
// attach view member function to the model 
CppEventHandler h = model.event.attach(view,& MyView::updateFun); 
// emit event and notify views 
model.event.notify(1); 
// detach the view from the model 
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.

// model class 
#include "CppSlot.h"

// define model class
class MyModel { 
public: 
 void modelChanged() 
 { 
    m_signal.emit_sig(1); 
 } 

 // signal with the signature of corresponding slot function 
// CppSignal<RETURNTYPE,1-ST ParameterType> 
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.

// view class 
class MyView { 
public: 

MyView() 
: m_slot(this,&MyView::onModelChanged) 
{}
 
int onModelChanged(int i) 
{ 
cout << "model changed:" << i << 
endl; 
return 1; 
} 

// slot with specified view type and signature of callback 
// function 
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.

// create model conaining CppSignal 
MyModel model; 
// create view with CppSlot 
MyView view; 
// connect signal to slot 
model.m_signal.connect(&view.m_slot); 
model.modelChanged();

Now we can create another view and connect it to the same model.

// create another view and connect it to the 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.

// use ResponseAccumulator function object 
// to collect responses from models 
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.

// now we can delete second model and it 
// automatically disconnect itself from the 
// model 
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++”.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
Canada Canada
Baranovsky Eduard has been a software developer for more then 10 years. He has an experence in image processing, computer graphics and distributed systems design.

Comments and Discussions

 
Generalalternative solution [modified] Pin
hell-citizen16-May-08 0:41
hell-citizen16-May-08 0:41 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.