Click here to Skip to main content
Click here to Skip to main content
 
Add your own
alternative version
Go to top

A new way to implement Delegate in C++

, 25 May 2007
Solving issues with some current implementations of Delegate in C++
#include "stdafx.h"
#include "Sophia/delegate.h"

namespace explanatory_1st_version
{
	class delegate2 // <void (int, char*)>
	{
	protected:
		class _never_exist_class;

		typedef void (_never_exist_class::*thiscall_method)(int, char*);
		typedef void (__cdecl _never_exist_class::*cdecl_method)(int, char*);
		typedef void (__stdcall _never_exist_class::*stdcall_method)(int, char*);
		typedef void (__fastcall _never_exist_class::*fastcall_method)(int, char*);
		typedef void (__cdecl *cdecl_function)(int, char*);
		typedef void (__stdcall *stdcall_function)(int, char*);
		typedef void (__fastcall *fastcall_function)(int, char*);

		enum delegate_type
		{
			thiscall_method_type,
			cdecl_method_type,
			stdcall_method_type,
			fastcall_method_type,
			cdecl_function_type,
			stdcall_function_type,
			fastcall_function_type,
		};

		class greatest_pointer_type
		{
			char never_use[sizeof(thiscall_method)];
		};

		delegate_type m_type;
		_never_exist_class* m_p;
		greatest_pointer_type m_fn;

	public:
		void operator()(int i, char* s)
		{
			switch(m_type)
			{
			case thiscall_method_type:
				return (m_p->*(*(thiscall_method*)(&m_fn)))(i, s);
			case cdecl_function_type:
				return (*(*(cdecl_function*)(&m_fn)))(i, s);
			default:
				// This is just a demo, don't implement for all cases
				throw;
			}
		}

		static int compare(const delegate2& _left, const delegate2& _right)
		{
			// first, compare pointer
			int result = memcmp(&_left.m_fn, &_right.m_fn, sizeof(_left.m_fn));
			if(0 == result)
			{
				// second, compare object
				result = (int) (((char*)_left.m_p) - ((char*)_right.m_p));
			}
			return result;
		}

		// constructor from __cdecl function
		delegate2(void (__cdecl *fn)(int, char*))
		{
			m_type = cdecl_function_type;
			m_p = 0;
			*reinterpret_cast<cdecl_function*>(&m_fn) = fn;
			// fill redundant bytes by ZERO for later comparison
			memset((char*)(&m_fn) + sizeof(fn), 0, sizeof(m_fn) - sizeof(fn));
		}

		// constructor from __thiscall method
		template<typename _T>
			delegate2(_T* p, void (_T::*fn)(int, char*))
		{
			m_type = thiscall_method_type;
			m_p = reinterpret_cast<_never_exist_class*>(p);

			///////////////////////////////////////////////////////////
			// WE WANT DOING THE FOLLWOING ASSIGNMENT
			// m_fn = fn
			// BUT HOW TO DO IN A STANDARD COMPLIANT AND PORTABLE WAY?
			// FOLLOW IS THE ANSWER
			///////////////////////////////////////////////////////////

			// forward reference
			class _another_never_exist_class_;
			typedef void (_another_never_exist_class_::*large_pointer_to_method)(int, char*);

			union
			{
				large_pointer_to_method _large_pointer;
				greatest_pointer_type _place_holder;
			};

			// Now tell compiler that '_another_never_exist_class_' is just a '_T' class
			class _another_never_exist_class_ : public _T {};

			// So the compiler will know how to translate the pointer to its larger form
			_large_pointer = fn;
			m_fn = _place_holder;
			COMPILE_TIME_ASSERT(sizeof(_large_pointer)==sizeof(_place_holder));
		}
	};
}

namespace explanatory_2nd_version
{
	// kind_of_class.cpp
	// This file is to demo about different kinds of pointer to members

	class dummy_base1 { };
	class dummy_base2 { };

	class dummy_s : dummy_base1 { };
	// Reach to here, the compiler will recognize dummy_s is a kind of �single inheritance�.
	typedef void (dummy_s::*pointer_to_dummy_s)(void);
	size_t size_of_single = sizeof(pointer_to_dummy_s);

	class dummy_m : dummy_base1, dummy_base2 { };
	// Reach to here, the compiler will recognize dummy_m is a kind of �multiple inheritance�.
	typedef void (dummy_m::*pointer_to_dummy_m)(void);
	size_t size_of_multi = sizeof(pointer_to_dummy_m);

	class dummy_v : virtual dummy_base1 { };
	// Reach to here, the compiler will recognize dummy_v is a kind of �virtual inheritance�.
	typedef void (dummy_v::*pointer_to_dummy_v)(void);
	size_t size_of_virtual = sizeof(pointer_to_dummy_v);

	class dummy_u;
	// forward reference, unknown at this time
	typedef void (dummy_u::*pointer_to_dummy_u)(void);
	size_t size_of_unknown = sizeof(pointer_to_dummy_u);

	void main()
	{
		printf("%d\n%d\n%d\n%d", size_of_single, size_of_multi, size_of_virtual, size_of_unknown);
	}

	class delegate_strategy // <void (int, char*)>
	{
	protected:
		class _never_exist_class;

		typedef void (_never_exist_class::*thiscall_method)(int, char*);
		typedef void (__cdecl _never_exist_class::*cdecl_method)(int, char*);
		typedef void (__stdcall _never_exist_class::*stdcall_method)(int, char*);
		typedef void (__fastcall _never_exist_class::*fastcall_method)(int, char*);
		typedef void (__cdecl *cdecl_function)(int, char*);
		typedef void (__stdcall *stdcall_function)(int, char*);
		typedef void (__fastcall *fastcall_function)(int, char*);

		class greatest_pointer_type
		{
			char never_use[sizeof(thiscall_method)];
		};

		_never_exist_class* m_p;
		greatest_pointer_type m_fn;

	public:

		// pure virtual function
		virtual void operator()(int, char*) const
		{
			throw std::exception();
		}
	};

	class delegate_cdecl_function_strategy : public delegate_strategy
	{
		// concrete strategy
		virtual void operator()(int i, char* s) const
		{
			return (*(*(cdecl_function*)(&m_fn)))(i, s);
		}

	public:

		// constructor
		delegate_cdecl_function_strategy(void (__cdecl *fn)(int, char*))
		{
			m_p = 0;
			*reinterpret_cast<cdecl_function*>(&m_fn) = fn;
			// fill redundant bytes by ZERO for later comparison
			memset((char*)(&m_fn) + sizeof(fn), 0, sizeof(m_fn) - sizeof(fn));
		}
	};

	class delegate_thiscall_method_strategy : public delegate_strategy
	{
		// concrete strategy
		virtual void operator()(int i, char* s) const
		{
			return (m_p->*(*(thiscall_method*)(&m_fn)))(i, s);
		}

	public:

		// constructor
		template<typename _T>
			delegate_thiscall_method_strategy(_T* p, void (_T::*fn)(int, char*))
		{
			m_p = reinterpret_cast<_never_exist_class*>(p);

			///////////////////////////////////////////////////////////
			// WE WANT DOING THE FOLLWOING ASSIGNMENT
			// m_fn = fn
			// BUT HOW TO DO IN A STANDARD COMPLIANT AND PORTABLE WAY?
			// FOLLOW IS THE ANSWER
			///////////////////////////////////////////////////////////

			// forward reference
			class _another_never_exist_class_;
			typedef void (_another_never_exist_class_::*large_pointer_to_method)(int, char*);

			union
			{
				large_pointer_to_method _large_pointer;
				greatest_pointer_type _place_holder;
			};

			// Now tell compiler that '_another_never_exist_class_' is just a '_T' class
			class _another_never_exist_class_ : public _T {};

			// So the compiler will know how to translate the pointer to its larger form
			_large_pointer = fn;
			m_fn = _place_holder;
			COMPILE_TIME_ASSERT(sizeof(_large_pointer)==sizeof(_place_holder));
		}
	};

	class delegate2 // <void (int, char*)>
	{
	protected:
		char m_strategy[sizeof(delegate_strategy)];

		const delegate_strategy& strategy() const
		{
			return *reinterpret_cast<delegate_strategy const*>(&m_strategy);
		}

	public:
		// constructor for __cdecl function
		delegate2(void (__cdecl *fn)(int, char*))
		{
			new (&m_strategy) delegate_cdecl_function_strategy(fn);
		}

		// constructor
		template<typename _T>
			delegate2(_T* p, void (_T::*fn)(int, char*))
		{
			new (&m_strategy) delegate_thiscall_method_strategy(p, fn);
		}

		// Syntax 01: (*delegate)(param...)
		delegate_strategy const& operator*() const throw()
		{
			return strategy();
		}

		// Syntax 02: delegate(param...)
		// Note: syntax 02 might be slower than syntax 01 in some cases
		void operator()(int i, char* s) const
		{
			return strategy()(i, s);
		}
	};

	struct CKnownClass
	{
		static void static_method(int i, char* s)
		{
			printf("static_method is called: (param1 = %d) (param2 = %s)\n", i, s);
		}

		void method(int i, char* s)
		{
			printf("method is called: (param1 = %d) (param2 = %s)\n", i, s);
		}
	};

	inline void Test()
	{
		// bind to static method
		delegate2 d1(&CKnownClass::static_method);

		// bind to method
		CKnownClass o;
		delegate2 d2(&o, &CKnownClass::method);

		// Syntax 1
		d1(10, "First call");
		d2(5, "Second call");

		// Syntax 2
		(*d1)(10, "First call");
		(*d2)(5, "Second call");
	}
}

namespace final_version
{
	using namespace sophia;

	// Base class with a virtual method
	struct BaseClass
	{
		virtual int virtual_method(int param) const
		{
			printf("We are in BaseClass: (param = %d)\n", param);
			return param;
		}
	};

	// A virtual-inheritance class
	struct DerivedClass : public virtual BaseClass
	{
		virtual int virtual_method(int param) const
		{
			printf("We are in DerivedClass: (param = %d)\n", param);
			return param;
		}
	};

	void Test()
	{
		// Assuming we have some objects
		const DerivedClass object;

		// Delegate declaration
		typedef sophia::delegate0<DWORD> MyDelegate0;
		typedef sophia::delegate1<int, int> MyDelegate1;
		typedef sophia::delegate2<void, int, int> MyDelegate2;

		// Constructor
		MyDelegate0 d0(&GetCurrentThreadId);
		MyDelegate1 d1(&object, &DerivedClass::virtual_method);
		MyDelegate1 d2; // null delegate
		MyDelegate2 dNull;

		// Determine size of a delegate instance
		printf("sizeof(delegate) = %d\n", sizeof(d0));

		// Compare between delegates even if they are different types
		assert(d2 == dNull);

		// Bind to a free function or a method
		d0.bind(&GetCurrentThreadId);
		d0 = &GetCurrentThreadId;
		d2.bind(&object, &DerivedClass::virtual_method);

		// Compare again after binding
		assert(d2 == d1);

		// Clear a delegate
		d2 = NULL; // or
		d2.clear();

		// Invoke with syntax 01
		d1(1000);

		// Invoke with syntax 02
		// This syntax is faster than syntax 01
		(*d1)(10000);

		// Swap between two delegates
		d2.swap(d1); // now d1 == NULL

		// Execute a null/empty delegate
		assert(d1.empty());
		try
		{
			d1(100);
		}
		catch(sophia::bad_function_call& e)
		{
			printf("Exception: %s\n    Try again: ", e.what());
			d2(0);
		}

		// Object life-time management
		// Case 1: we want the object is cloned
		d1.bind(&object, &DerivedClass::virtual_method, clone_option<heap_clone_allocator>(true));

		// Object life-time management
		// Case 2: we DO NOT want the object is cloned when binding
		for(int i=0; i<100; ++i)
		{
			DerivedClass* pObject = new DerivedClass();
			d1.bind(pObject, &DerivedClass::virtual_method, clone_option<heap_clone_allocator>(false));
			d1(100);
		}
	}
}

int main(int argc, char* args[])
{
	printf("\n--------------------Explanatory version--------------\n\n");
	explanatory_2nd_version::Test();
	printf("\n--------------------Final version--------------------\n\n");
	final_version::Test();

	return 0;
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Quynh Nguyen
Global Cybersoft (Vietnam)
Vietnam Vietnam
Quynh Nguyen is a Vietnamese who has worked for 7 years in Software Outsourcing area. Currently, he works for Global Cybersoft (Vietnam) Ltd. as a Project Manager in Factory Automation division.
 
In the first day learning C language in university, he had soon switched to Assembly language because he was not able to understand why people cannot get address of a constant as with a variable. With that stupid starting, he had spent a lot of his time with Assembly language during the time he was in university.
 
Now he is interesting in Software Development Process, Software Architecture and Design Pattern… He especially indulges in highly concurrent software.

| Advertise | Privacy | Mobile
Web01 | 2.8.140926.1 | Last Updated 25 May 2007
Article Copyright 2007 by Quynh Nguyen
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid