Click here to Skip to main content
15,884,388 members
Articles / Programming Languages / C++

A new way to implement Delegate in C++

Rate me:
Please Sign up or sign in to vote.
4.88/5 (28 votes)
25 May 2007CPOL11 min read 184.5K   918   110  
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 object_type>
			delegate2(object_type* p, void (object_type::*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*);
            
			COMPILE_TIME_ASSERT(sizeof(large_pointer_to_method)==sizeof(greatest_pointer_type ));

			// Now tell compiler that '_another_never_exist_class_' is just a 'T' class
			class _another_never_exist_class_ : public object_type {};
        
			reinterpret_cast<large_pointer_to_method&>(m_fn) = fn;

			// Double checking to make sure the compiler doesn't change its mind :-)
			COMPILE_TIME_ASSERT(sizeof(large_pointer_to_method)==sizeof(greatest_pointer_type ));
		}
	};
}

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 object_type>
			delegate_thiscall_method_strategy(object_type* p, void (object_type::*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*);
            
			COMPILE_TIME_ASSERT(sizeof(large_pointer_to_method)==sizeof(greatest_pointer_type ));

			// Now tell compiler that '_another_never_exist_class_' is just a 'T' class
			class _another_never_exist_class_ : public object_type {};
        
			reinterpret_cast<large_pointer_to_method&>(m_fn) = fn;

			// Double checking to make sure the compiler doesn't change its mind :-)
			COMPILE_TIME_ASSERT(sizeof(large_pointer_to_method)==sizeof(greatest_pointer_type ));
		}
	};

	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 object_type>
			delegate2(object_type* p, void (object_type::*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;
		}

		char relaxed_method(long param)
		{
			printf("We are in relaxed_method: (param = %d)\n", param);
			return 0;
		}
	};

	// 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
		DerivedClass object;

		// Delegate declaration
		typedef sophia::delegate0<DWORD> MyDelegate0;
		typedef sophia::delegate1<int, int> MyDelegate1;
		typedef sophia::delegate4<void, int, int, long, char> AnotherDelegateType;

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

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

		// 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);

		// RELAXED delegate
		d1.bind(&object, &DerivedClass::relaxed_method);
		(*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)


Written By
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.

Comments and Discussions