Generic Delegates: C++ Implementation

Hi Geeks, Hope that you’re fine since the last post and doing well with your bugs and exceptions.
Le’ts get to the topic directly, I was implementing some dirty stuff using C++, and I had to implement a simple event handler, and suddenly some dirty thoughts jumped into my head and asked myself about implementing sort of generic Delegate.

If we were thinking about implementation of Delegate we have to worry about 2 things the first one the return type of the Delegate and the second is argument list, so we can make something like this (Basic Implementation)

#include <iostream>
class Delegate
{
	typedef void(*Callback)(voidp_ptrToObjint parm);
 
public:
	Delegate()
	{}
	Delegate(voidp_objCallback p_func) : m_ptrToObject(p_obj), m_ptrToFunction(p_func)
	{}
	template <class Tvoid (T::*TMethod)(int)>
	//function for making a delegate object
	static Delegate MakeDelegate(T* p_obj)
	{
		Delegate delegate(p_obj, &Call<TTMethod>);
		return delegate;
	}
	void operator()(int p_param)
	{
 
		(*m_ptrToFunction)(m_ptrToObjectp_param);
	}
private:
	Callback   m_ptrToFunction;
	void	   *m_ptrToObject;
 
	template <class Tvoid (T::*TMethod)(int)>
	//Helper function for casting the untyped stored pointer (void*)
	static void Call(void* p_objint param)
	{
		Tptr = static_cast<T*>(p_obj);
		return (ptr->*TMethod)(param);
	}
};
 
//Testing Class
class Test
{
public:
	void Test_foo(int val)
	{
		printf("Test_foo called with value =%d\n"val);
	}
};
 
// Entry point 
void main(void)
{
	Test testObj;
 
	Delegate delegate = Delegate::MakeDelegate < Test, &Test::Test_foo>(&testObj);
	delegate(13);
}

ِAfter checking this implementation, we’ll find that it works with a specific function signature for the delegate function, simply a function that takes a parameter with type (int) and returns (void).

The second implementation is more generic than the previous one, C++ can support us to make a generic return types and argument types , let’s dirty our fingers with some code.

#include <iostream>
template<typename TReturnTypetypename TParam>
class Delegate
{
	typedef TReturnType(*Callback)(voidp_ptrToObjTParam parm);
 
public:
	Delegate()
	{}
	Delegate(voidp_objCallback p_func) : m_ptrToObject(p_obj), m_ptrToFunction(p_func)
	{}
	template <class TTReturnType(T::*TMethod)(TParam)>
	//function for making a delegate object
	static Delegate MakeDelegate(TptrToObj) 
	{
		Delegate delegate(ptrToObj, &Call<TTMethod>);
		return delegate;
	}
	TReturnType operator()(TParam p_param) 
	{
		return (*m_ptrToFunction)(m_ptrToObjectp_param);
	}
private:
	Callback	m_ptrToFunction;
	void	   *m_ptrToObject;
 
	template <class TTReturnType(T::*TMethod)(TParam)>
	//Helper function for casting the untyped stored pointer (void*)
	static TReturnType Call(voidp_objTParam param)
	{
		Tptr = static_cast<T*>(p_obj);
		return (ptr->*TMethod)(param);
	}
};
 
//Testing Class
class Test
{
public:
	int Test_foo(int val)
	{
		int res = val + 10;
		printf("Test_foo returned %d\n"res);
		return res;
	}
};
 
// Entry point 
void main(void)
{
	Test testObj;
	Delegate<intint>  delegate = Delegate<intint> ::MakeDelegate<Test, &Test::Test_foo>(&testObj);
	delegate(20);
}

Hmmm, as you can see, it’s little bit generic, but we still controlled with limited number of arguments, that’s not the target. we need to reach to dynamic return type and dynamic argument list.

Ok, I’ll tell you something about me, some days ago I was reading a book named “Modern C++ design”, seems old but golden one (قطعة من الجنة) and I was reading specifically the part related to Functors’ implementation, and I knew about something in C++ named TypeLists, you can read about it from the book and you can find its implementation here , and after little efforts of online searching I accidentally found something that may be a golden solution for my problem, it’s variadic templates.

variadic templates are templates that accept zero or more template arguments (non-types, types, templates) and supported in C++ since C++11, and it’s considered as a solution for a long standing C++ problem.

template<typename ... TParam>

I think the solution of our problem is clear now, and now it’s the time to dirty our hands with a real generic version of Delegates

#include <iostream>
template<typename TReturnTypetypename ... TParams>
class Delegate
{
	typedef TReturnType(*Callback)(voidp_ptrToObjTParams...);
 
public:
	Delegate()
	{}
	Delegate(voidp_objCallback p_func) : m_ptrToObject(p_obj), m_ptrToFunction(p_func)
	{}
	template <class TTReturnType(T::*TMethod)(TParams...)>
	//function for making a delegate object
	static Delegate MakeDelegate(TptrToObj)
	{
		Delegate delegate(ptrToObj, &Call<TTMethod>);
		return delegate;
	}
	TReturnType operator()(TParams...p_paramList)
	{
		return (*m_ptrToFunction)(m_ptrToObjectp_paramList...);
	}
private:
	Callback	m_ptrToFunction;
	void	   *m_ptrToObject;
 
	template <class TTReturnType(T::*TMethod)(TParams...)>
	//Helper function for casting the untyped stored pointer (void*)
	static TReturnType Call(voidp_objTParams...p_paramList)
	{
		Tptr = static_cast<T*>(p_obj);
		return (ptr->*TMethod)(p_paramList...);
	}
};
 
//Testing Class
class Test
{
public:
	int Test_foo(int val)
	{
		int res = val + 10;
		printf("Test_foo returned %d\n"res);
		return res;
	}
	int Test_bar(int xint yint z)
	{
		z = x*y;
		printf("Test_bar returned %d\n"z);
		return z;
	}
};
 
// Entry point 
void main(void)
{
	Test testObj;
	
	auto delegate = Delegate<intint>::MakeDelegate<Test, &Test::Test_foo>(&testObj);
	delegate(20);
	auto delegate1 = Delegate<intint,int,int>::MakeDelegate<Test, &Test::Test_bar>(&testObj);
	delegate1(10, 20, 30);
}

Ohh, finally it’s done, finally we have Delegate implementation for different functions signatures.

A more handsome implementation here on github that supports both Multicast and Unicast delegates.

References:
Advertisements