Introduction
The arrival of .NET brought some very nice approaches and techniques. Of course, one main reason of .NET code efficiency (I mean from a developer's scope) is that .NET languages are fully and exclusively object oriented.
One very nice attribute of .NET (and especially of C#) is delegates. Delegates are defined as member variables, and can hold references to functions.
Can C++ (and I meal real (native) C++) simulate this approach? While I am developing a very nice project in MFC, I realized that I can do (or even simulate) everything (with C++) that all other languages do. The only thing that I have noticed C++ does not have are function definitions in functions, as Delphi and Pascal have.
Anyway, because I wanted to get rid of non object oriented event handlings that MFC have (DECLARE MAPS... etc.), I tried to develop a template class that simulates C# delegates. And it wasn't so difficult.
The Class
The class is as follows:
template <class FP, class SENDER,
class ARG1=int, class ARG2=int, class ARG3=int, class ARG4=int,
class ARG5=int, class ARG6=int, class ARG7=int, class ARG8=int>
class GDDelegateS
{
public:
struct FPDATA
{
FP fp; void *data;
};
public:
GDDelegateS()
{
m_List = NULL;
m_ListCount = 0;
}
virtual ~GDDelegateS()
{
Clear();
}
protected:
FPDATA *m_List;
int m_ListCount;
public:
void Add(FP funct, void *data)
{
FPDATA *nList = new FPDATA[m_ListCount+1];
FPDATA *f=NULL;
for(int i=0;i<m_ListCount;i++)
{
if(funct==m_List[i].fp && data == m_List[i].data)
{
delete []nList;
return;
}
nList[i].data = m_List[i].data;
nList[i].fp = m_List[i].fp;
}
nList[i].data = data;
nList[i].fp = funct;
if(m_List)
delete m_List;
m_List = nList;
m_ListCount++;
}
void Send(SENDER sender)
{
for(int i=0;i<m_ListCount;i++)
(*m_List[i].fp)(sender, m_List[i].data);
}
void Send(SENDER sender, ARG1 arg1)
{
for(int i=0;i<m_ListCount;i++)
(*m_List[i].fp)(sender, m_List[i].data, arg1);
}
void Send(SENDER sender, ARG1 arg1, ARG2 arg2)
{
for(int i=0;i<m_ListCount;i++)
(*m_List[i].fp)(sender, m_List[i]data, arg1, arg2);
}
void Send(SENDER sender, ARG1 arg1, ARG2 arg2, ARG3 arg3)
{
for(int i=0;i<m_ListCount;i++)
(*m_List[i].fp)(sender, m_List[i]data, arg1, arg2, arg3);
}
void Send(SENDER sender, ARG1 arg1, ARG2 arg2, ARG3 arg3, ARG4 arg4)
{
for(int i=0;i<m_ListCount;i++)
(*m_List[i].fp)(sender, m_List[i].data, arg1, arg2, arg3, arg4);
}
void Send(SENDER sender, ARG1 arg1, ARG1 arg2,
ARG3 arg3, ARG4 arg4, ARG5 arg5)
{
for(int i=0;i<m_ListCount;i++)
(*m_List[i].fp)(sender, m_List[i].data, arg1, arg2, arg3, arg4, arg5);
}
void Clear()
{
if(m_List)
{
delete []m_List;
m_ListCount = 0;
m_List = NULL;
}
}
};
Using the Code
As you can see, this class has 10 template arguments. The first two are required, and are:
- the desired function type to handle the event.
- The type of the sender object (
void *
if it is not required).
The function prototype must have also at least two arguments:
- The type of the sender object (of course, must be identical to the second template argument).
- A void pointer. This pointer points to (extra) data that is being passed to the
GDDelegiateS
variable when the event handling function is defined as an event handler.
The function prototype can have up to 10 arguments. The third argument of the function prototype must be identical to the template third argument, and the fourth and the fifth and so on.
The function prototype can not be a member of a class. That's why I call this class GDDelegateS
.
Let's see an example to understand better:
typedef void (*_fpSimpleFunct)(void* , void* );
void DoSomething(void *sender, void *extra_data);
GDDelegateS<_fpSimpleFunct, void *> m_ASimpleDelegate;
Suppose we have the main
function:
void main()
{
m_ASimpleDelegate.Add((_fpSimpleFunct)DoSomething,
NULL);
m_ASimpleDelegate.Send(NULL);
}
void DoSomething(void *sender, void *extra_data)
{
cout<<"Here is the delegate!";
}
As you can see:
- This class keeps more than one function pointer.
- If you try to pass the same function with the same data twice,
Add
won't do it. - The
Send
function you invoke must have one argument less than the function's prototype. - Use the
Clear
function to reset the prototype. - I did not implement
Send
for 6, 7, and 8 arguments. It is not difficult for you to do it.
I am sure you know that delegates can be used in several different ways. Thank you a lot for your time.
My name is George Drivas.
The programming was for me the main job. Now I am coding mostly for fun. My main language is C\C++ and I am using the most common developments tools. I am interesting mostly for the MFC development. This site is (in my opinion) the best for MFC development. I hope that I will be able to contribute in this community.