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

A new way to implement Delegate in C++

, 25 May 2007
Solving issues with some current implementations of Delegate in C++
#if SOPHIA_PARAM_COUNT > 0
#define SOPHIA_COMMA ,
#else
#define SOPHIA_COMMA 
#endif // SOPHIA_PARAM_COUNT

namespace SOPHIA_DETAIL_NAMESPACE
{
	template<typename R>
	struct relax_if_void // R != void
	{
		template<typename base_class, typename TFunction SOPHIA_COMMA SOPHIA_TEMPLATE_PARAMS>
		struct function_strategy : public base_class
		{
			virtual R operator() (SOPHIA_PARAMS) const
			{
				return (*(*(TFunction*)(&this->m_fn)))(SOPHIA_ARGS);
			}
		};

		template<typename base_class, typename calling_convention SOPHIA_COMMA SOPHIA_TEMPLATE_PARAMS>
		struct method_strategy : public base_class
		{
			virtual R operator() (SOPHIA_PARAMS) const
			{
				return (this->m_object_ptr->*(*(typename calling_convention::rebind::method*)(&this->m_fn)))(SOPHIA_ARGS);
			}
		};
	};

	template<>
	struct relax_if_void<void> // R == void
	{
		template<typename base_class, typename TFunction SOPHIA_COMMA SOPHIA_TEMPLATE_PARAMS>
		struct function_strategy : public base_class
		{
			virtual void operator() (SOPHIA_PARAMS) const
			{
				(*(*(TFunction*)(&this->m_fn)))(SOPHIA_ARGS);
			}
		};

		template<typename base_class, typename calling_convention SOPHIA_COMMA SOPHIA_TEMPLATE_PARAMS>
		struct method_strategy : public base_class
		{
			virtual void operator() (SOPHIA_PARAMS) const
			{
				(this->m_object_ptr->*(*(typename calling_convention::rebind::method*)(&this->m_fn)))(SOPHIA_ARGS);
			}
		};
	};

	template<typename _object_type, typename R SOPHIA_COMMA SOPHIA_TEMPLATE_PARAMS>
	struct thiscall_convention
	{
		typedef _object_type object_type;
		typedef R (_object_type::*method)(SOPHIA_TEMPLATE_ARGS);
		typedef R (_object_type::*const_method)(SOPHIA_TEMPLATE_ARGS) const;
		typedef thiscall_convention<_never_exist_class_, R SOPHIA_COMMA SOPHIA_TEMPLATE_ARGS> rebind;
	};

	template<typename _object_type, typename R SOPHIA_COMMA SOPHIA_TEMPLATE_PARAMS>
	struct cdecl_convention
	{
		typedef _object_type object_type;
		typedef R (__cdecl _object_type::*method)(SOPHIA_TEMPLATE_ARGS);
		typedef R (__cdecl _object_type::*const_method)(SOPHIA_TEMPLATE_ARGS) const;
		typedef cdecl_convention<_never_exist_class_, R SOPHIA_COMMA SOPHIA_TEMPLATE_ARGS> rebind;
	};

	template<typename _object_type, typename R SOPHIA_COMMA SOPHIA_TEMPLATE_PARAMS>
	struct stdcall_convention
	{
		typedef _object_type object_type;
		typedef R (__stdcall _object_type::*method)(SOPHIA_TEMPLATE_ARGS);
		typedef R (__stdcall _object_type::*const_method)(SOPHIA_TEMPLATE_ARGS) const;
		typedef stdcall_convention<_never_exist_class_, R SOPHIA_COMMA SOPHIA_TEMPLATE_ARGS> rebind;
	};

	template<typename _object_type, typename R SOPHIA_COMMA SOPHIA_TEMPLATE_PARAMS>
	struct fastcall_convention
	{
		typedef _object_type object_type;
		typedef R (__fastcall _object_type::*method)(SOPHIA_TEMPLATE_ARGS);
		typedef R (__fastcall _object_type::*const_method)(SOPHIA_TEMPLATE_ARGS) const;
		typedef fastcall_convention<_never_exist_class_, R SOPHIA_COMMA SOPHIA_TEMPLATE_ARGS> rebind;
	};
}

template<typename R SOPHIA_COMMA SOPHIA_TEMPLATE_PARAMS>
class SOPHIA_DELEGATE_CLASSNAME : public delegate_root
{
protected:

	typedef clone_option<view_clone_allocator>::no_clone_strategy no_clone_strategy;

	struct invokable_strategy : public no_clone_strategy
	{
		virtual R operator() (SOPHIA_PARAMS) const = 0;
	};

	struct null_strategy : public no_clone_strategy
	{
		null_strategy()
		{
			this->init_null();
		}
		virtual R operator() (SOPHIA_PARAMS) const
		{
			throw bad_function_call();
		}
		virtual bool is_empty() const throw()
		{
			return true;
		}
	};

	template<typename TFunction>
	struct function_strategy : public SOPHIA_DETAIL_NAMESPACE::relax_if_void<R>::template
		function_strategy<no_clone_strategy, TFunction SOPHIA_COMMA SOPHIA_TEMPLATE_ARGS>
	{
		function_strategy(TFunction fn)
		{
			COMPILE_TIME_ASSERT(sizeof(strategy_root::pointer_to_function)==sizeof(TFunction));
			this->init_function(reinterpret_cast<strategy_root::pointer_to_function&>(fn));
		}
	};

	template<typename clone_strategy_type, typename calling_convention>
	struct method_strategy : public SOPHIA_DETAIL_NAMESPACE::relax_if_void<R>::template
		method_strategy<clone_strategy_type, calling_convention SOPHIA_COMMA SOPHIA_TEMPLATE_ARGS>
	{
		method_strategy(typename calling_convention::method fn,
			typename calling_convention::object_type & r)
		{
			this->m_object_ptr = reinterpret_cast<_never_exist_class_*>(&r);

			// translate pointer-to-member to its large form
			class _another_never_exist_class_;
			typedef typename calling_convention::object_type object_type;
			typedef	void (object_type::*small_pointer_to_method)();
			typedef void (_another_never_exist_class_::*large_pointer_to_method)();

			COMPILE_TIME_ASSERT(
				(sizeof(small_pointer_to_method)==sizeof(typename calling_convention::method)) &&
				(sizeof(large_pointer_to_method)==sizeof(strategy_root::greatest_pointer_type)));

			// Now tell the compiler about '_another_never_exist_class_"
			class _another_never_exist_class_ : public object_type {};

			// Double checking to make sure the compiler doesn't change its mind :-)
			COMPILE_TIME_ASSERT(
				(sizeof(small_pointer_to_method)==sizeof(typename calling_convention::method)) &&
				(sizeof(large_pointer_to_method)==sizeof(strategy_root::greatest_pointer_type)));

			reinterpret_cast<large_pointer_to_method&>(this->m_fn) = reinterpret_cast<const small_pointer_to_method&>(fn);
		}
	};

	template<typename clone_strategy_type, typename calling_convention>
	struct const_method_strategy : public method_strategy<clone_strategy_type, calling_convention>
	{
		// treat const method just as non-const method
		// ==> forward all parameter to its base class after some necessary castings
		const_method_strategy(typename calling_convention::const_method fn,
			typename calling_convention::object_type const& r)
			: method_strategy<clone_strategy_type, calling_convention>(
			reinterpret_cast<typename calling_convention::method&>(fn),
			const_cast<typename calling_convention::object_type&>(r))
		{
		}
	};

	// construct delegate from 'thiscall' method
	template<typename base_type, typename R_R SOPHIA_COMMA SOPHIA_TEMPLATE_PARAMS_RELAX, typename object_type, typename clone_info>
		void construct_method(R_R (base_type::*fn)(SOPHIA_TEMPLATE_ARGS_RELAX), object_type& r, clone_info info) throw()
	{
		new (&this->m_strategy) method_strategy<typename clone_info::template strategy<object_type, base_type>,
			SOPHIA_DETAIL_NAMESPACE::thiscall_convention<base_type, R_R SOPHIA_COMMA SOPHIA_TEMPLATE_ARGS_RELAX> >(fn, r);
		if(info) this->strategy().object_clone();
	}

	// construct delegate from 'thiscall' const-method
	template<typename base_type, typename R_R SOPHIA_COMMA SOPHIA_TEMPLATE_PARAMS_RELAX, typename object_type, typename clone_info>
		void construct_method(R_R (base_type::*fn)(SOPHIA_TEMPLATE_ARGS_RELAX) const, object_type& r, clone_info info) throw()
	{
		new (&this->m_strategy) const_method_strategy<typename clone_info::template strategy<object_type, base_type>,
			SOPHIA_DETAIL_NAMESPACE::thiscall_convention<base_type, R_R SOPHIA_COMMA SOPHIA_TEMPLATE_ARGS_RELAX> >(fn, r);
		if(info) this->strategy().object_clone();
	}

#if defined(SOPHIA_SUPPORT_CALLING_CONVENTION_FOR_METHOD)

	// construct delegate from 'cdecl' method
	template<typename base_type, typename R_R SOPHIA_COMMA SOPHIA_TEMPLATE_PARAMS_RELAX, typename object_type, typename clone_info>
		void construct_method(R_R (__cdecl base_type::*fn)(SOPHIA_TEMPLATE_ARGS_RELAX), object_type& r, clone_info info) throw()
	{
		new (&this->m_strategy) method_strategy<typename clone_info::template strategy<object_type, base_type>,
			SOPHIA_DETAIL_NAMESPACE::cdecl_convention<base_type, R_R SOPHIA_COMMA SOPHIA_TEMPLATE_ARGS_RELAX> >(fn, r);
		if(info) this->strategy().object_clone();
	}

	// construct delegate from 'cdecl' const-method
	template<typename base_type, typename R_R SOPHIA_COMMA SOPHIA_TEMPLATE_PARAMS_RELAX, typename object_type, typename clone_info>
		void construct_method(R_R (__cdecl base_type::*fn)(SOPHIA_TEMPLATE_ARGS_RELAX) const, object_type& r, clone_info info) throw()
	{
		new (&this->m_strategy) const_method_strategy<typename clone_info::template strategy<object_type, base_type>,
			SOPHIA_DETAIL_NAMESPACE::cdecl_convention<base_type, R_R SOPHIA_COMMA SOPHIA_TEMPLATE_ARGS_RELAX> >(fn, r);
		if(info) this->strategy().object_clone();
	}

	// construct delegate from 'stdcall' method
	template<typename base_type, typename R_R SOPHIA_COMMA SOPHIA_TEMPLATE_PARAMS_RELAX, typename object_type, typename clone_info>
		void construct_method(R_R (__stdcall base_type::*fn)(SOPHIA_TEMPLATE_ARGS_RELAX), object_type& r, clone_info info) throw()
	{
		new (&this->m_strategy) method_strategy<typename clone_info::template strategy<object_type, base_type>,
			SOPHIA_DETAIL_NAMESPACE::stdcall_convention<base_type, R_R SOPHIA_COMMA SOPHIA_TEMPLATE_ARGS_RELAX> >(fn, r);
		if(info) this->strategy().object_clone();
	}

	// construct delegate from 'stdcall' const-method
	template<typename base_type, typename R_R SOPHIA_COMMA SOPHIA_TEMPLATE_PARAMS_RELAX, typename object_type, typename clone_info>
		void construct_method(R_R (__stdcall base_type::*fn)(SOPHIA_TEMPLATE_ARGS_RELAX) const, object_type& r, clone_info info) throw()
	{
		new (&this->m_strategy) const_method_strategy<typename clone_info::template strategy<object_type, base_type>,
			SOPHIA_DETAIL_NAMESPACE::stdcall_convention<base_type, R_R SOPHIA_COMMA SOPHIA_TEMPLATE_ARGS_RELAX> >(fn, r);
		if(info) this->strategy().object_clone();
	}

	// construct delegate from 'fastcall' method
	template<typename base_type, typename R_R SOPHIA_COMMA SOPHIA_TEMPLATE_PARAMS_RELAX, typename object_type, typename clone_info>
		void construct_method(R_R (__fastcall base_type::*fn)(SOPHIA_TEMPLATE_ARGS_RELAX), object_type& r, clone_info info) throw()
	{
		new (&this->m_strategy) method_strategy<typename clone_info::template strategy<object_type, base_type>,
			SOPHIA_DETAIL_NAMESPACE::fastcall_convention<base_type, R_R SOPHIA_COMMA SOPHIA_TEMPLATE_ARGS_RELAX> >(fn, r);
		if(info) this->strategy().object_clone();
	}

	// construct delegate from 'fastcall' const-method
	template<typename base_type, typename R_R SOPHIA_COMMA SOPHIA_TEMPLATE_PARAMS_RELAX, typename object_type, typename clone_info>
		void construct_method(R_R (__fastcall base_type::*fn)(SOPHIA_TEMPLATE_ARGS_RELAX) const, object_type& r, clone_info info) throw()
	{
		new (&this->m_strategy) const_method_strategy<typename clone_info::template strategy<object_type, base_type>,
			SOPHIA_DETAIL_NAMESPACE::fastcall_convention<base_type, R_R SOPHIA_COMMA SOPHIA_TEMPLATE_ARGS_RELAX> >(fn, r);
		if(info) this->strategy().object_clone();
	}
#endif //defined(SOPHIA_SUPPORT_CALLING_CONVENTION_FOR_METHOD)

public:

	// default constructor
	SOPHIA_DELEGATE_CLASSNAME() throw()
	{
		new (&this->m_strategy) null_strategy();
	}

	// copy constructor
	SOPHIA_DELEGATE_CLASSNAME(SOPHIA_DELEGATE_CLASSNAME const & _other) throw()
	{
		this->m_strategy = _other.m_strategy; // do bit-wise copy
		this->strategy().object_clone(); // then clone object
	}

	// operator =
	SOPHIA_DELEGATE_CLASSNAME& operator=(SOPHIA_DELEGATE_CLASSNAME const & _other) throw()
	{
		delegate_root old(*this); // cleaning up automatically on return 
		new (this) SOPHIA_DELEGATE_CLASSNAME(_other);;
		return *this;
	}

	// swap between 2 delegates
	void swap(SOPHIA_DELEGATE_CLASSNAME& _other) throw()
	{
		// all operations are just bit-wise copy
		strategy_place_holder temp(this->m_strategy);
		this->m_strategy = _other.m_strategy;
		_other.m_strategy = temp;
	}

	// reset to null
	void clear() throw()
	{
		this->strategy().object_free();
		new (&this->m_strategy) null_strategy();
	}

	// operator = NULL
	const SOPHIA_DELEGATE_CLASSNAME& operator=(clear_type*)
	{
		this->clear();
		return *this;
	}

	// Syntax 01: (*delegate)(param...)
	invokable_strategy const& operator*() const throw()
	{
		return static_cast<invokable_strategy const&>(this->strategy());
	}

	// Syntax 02: delegate(param...)
	// Note: syntax 02 might be slower than syntax 01 in some cases
	R operator()(SOPHIA_PARAMS) const
	{
		return static_cast<invokable_strategy const&>(this->strategy())(SOPHIA_ARGS);
	}

	// constructor for function
	template<typename TFunction>
		SOPHIA_DELEGATE_CLASSNAME(TFunction fn)
	{
		new (&this->m_strategy) function_strategy<TFunction>(fn);
	}

	// bind to function
	template<typename TFunction>
		void bind(TFunction fn)
	{
		this->strategy().object_free();
		new (&this->m_strategy) function_strategy<TFunction>(fn);
	}

	// constructor for method
	template<typename TMethod, typename object_type>
		SOPHIA_DELEGATE_CLASSNAME(object_type* p, TMethod fn)
	{
		this->construct_method(fn, *p, clone_option<view_clone_allocator>());
	}

	// bind to method
	template<typename TMethod, typename object_type>
		void bind(object_type* p, TMethod fn)
	{
		this->strategy().object_free();
		this->construct_method(fn, *p, clone_option<view_clone_allocator>());
	}

	// constructor for method using clone-allocator
	template<typename TMethod, typename object_type, typename clone_info>
		SOPHIA_DELEGATE_CLASSNAME(object_type* p, TMethod fn, clone_info info)
	{
		this->construct_method(fn, *p, info);
	}

	// bind to method using clone-allocator
	template<typename TMethod, typename object_type, typename clone_info>
		void bind(object_type* p, TMethod fn, clone_info info)
	{
		this->strategy().object_free();
		this->construct_method(fn, *p, info);
	}
};

#undef SOPHIA_COMMA

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)

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
Web02 | 2.8.140709.1 | Last Updated 25 May 2007
Article Copyright 2007 by Quynh Nguyen
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid