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++
#define SOPHIA_TEMPLATE_PARAMS    SOPHIA_MAKE_PARAMS1(SOPHIA_PARAM_COUNT,typename A)
// typename A0, class A1, class A2, ...
#define SOPHIA_TEMPLATE_ARGS      SOPHIA_MAKE_PARAMS1(SOPHIA_PARAM_COUNT,A)
// A0, A1, A2, ...
#define SOPHIA_PARAMS             SOPHIA_MAKE_PARAMS2(SOPHIA_PARAM_COUNT,A,a)
// A0 a0, A1 a1, A2 a2, ...
#define SOPHIA_ARGS               SOPHIA_MAKE_PARAMS1(SOPHIA_PARAM_COUNT,a)
// a0, a1, a2, ...

#if SOPHIA_PARAM_COUNT > 0
#define SOPHIA_COMMA ,
#else
#define SOPHIA_COMMA 
#endif // SOPHIA_PARAM_COUNT

template<typename R SOPHIA_COMMA SOPHIA_TEMPLATE_PARAMS>
class SOPHIA_DELEGATE_CLASSNAME : public delegate_root
{
	template<typename _T>
	struct thiscall_convention
	{
		typedef _T object_type;
		typedef R (_T::*method)(SOPHIA_TEMPLATE_ARGS);
		typedef R (_T::*const_method)(SOPHIA_TEMPLATE_ARGS) const;
		typedef thiscall_convention<_never_exist_class_> rebind;
	};

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

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

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

	template<class strategy_type>
	struct strategy_t : public strategy_type
	{
		virtual R operator() (SOPHIA_PARAMS) const = 0;
	};
	typedef strategy_t<clone_option_never_clone::strategy> invokable_strategy;

	struct null_strategy : public invokable_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 invokable_strategy
	{
		function_strategy(TFunction fn)
		{
			this->init_function(fn);
		}

		virtual R operator() (SOPHIA_PARAMS) const
		{
			return (*(*(TFunction*)(&this->m_fn)))(SOPHIA_ARGS);
		}
	};

	template<class calling_convention, class strategy_type>
	struct method_strategy : public strategy_t<strategy_type>
	{
		using strategy_type::m_fn;
		using strategy_type::m_object_ptr;

		method_strategy(typename calling_convention::object_type* p,
			typename calling_convention::method fn,
			bool init_clone_object)
		{
			this->init_method(p, fn, init_clone_object);
		}

		method_strategy(typename calling_convention::object_type const* p,
			typename calling_convention::const_method fn,
			bool init_clone_object)
		{
			this->init_method(const_cast<typename calling_convention::object_type*>(p), fn, init_clone_object);
		}

		virtual R operator() (SOPHIA_PARAMS) const
		{
			typedef typename calling_convention::rebind rebind;
			COMPILE_TIME_ASSERT(sizeof(typename rebind::method)==sizeof(m_fn));
			return (this->m_object_ptr->*(*(typename rebind::method*)(&this->m_fn)))(SOPHIA_ARGS);
		}
	};

	// construct delegate from 'thiscall' method
	template<typename _T, typename clone_info>
		void construct_method(_T* p, typename thiscall_convention<_T>::method fn, clone_info info) throw()
	{
		new (&this->m_strategy) method_strategy<thiscall_convention<_T>, typename clone_info::strategy>(p, fn, info);
	}

	// construct delegate from 'thiscall' const method
	template<typename _T, typename clone_info>
		void construct_method(_T const* p, typename thiscall_convention<_T>::const_method fn, clone_info info) throw()
	{
		new (&this->m_strategy) method_strategy<thiscall_convention<_T>, typename clone_info::strategy>(p, fn, info);
	}

#if defined(SOPHIA_SUPPORT_CALLING_CONVENTION_FOR_METHOD)

	// construct delegate from 'cdecl' method
	template<typename _T, typename clone_info>
		void construct_method(_T* p, typename cdecl_convention<_T>::method fn, clone_info info) throw()
	{
		new (&this->m_strategy) method_strategy<cdecl_convention<_T>, typename clone_info::strategy>(p, fn, info);
	}

	// construct delegate from 'cdecl' const method
	template<typename _T, typename clone_info>
		void construct_method(_T const* p, typename cdecl_convention<_T>::const_method fn, clone_info info) throw()
	{
		new (&this->m_strategy) method_strategy<cdecl_convention<_T>, typename clone_info::strategy>(p, fn, info);
	}

	// construct delegate from 'stdcall' method
	template<typename _T, typename clone_info>
		void construct_method(_T* p, typename stdcall_convention<_T>::method fn, clone_info info) throw()
	{
		new (&this->m_strategy) method_strategy<stdcall_convention<_T>, typename clone_info::strategy>(p, fn, info);
	}

	// construct delegate from 'stdcall' const method
	template<typename _T, typename clone_info>
		void construct_method(_T const* p, typename stdcall_convention<_T>::const_method fn, clone_info info) throw()
	{
		new (&this->m_strategy) method_strategy<stdcall_convention<_T>, typename clone_info::strategy>(p, fn, info);
	}

	// construct delegate from 'fastcall' method
	template<typename _T, typename clone_info>
		void construct_method(_T* p, typename fastcall_convention<_T>::method fn, clone_info info) throw()
	{
		new (&this->m_strategy) method_strategy<fastcall_convention<_T>, typename clone_info::strategy>(p, fn, info);
	}

	// construct delegate from 'fastcall' const method
	template<typename _T, typename clone_info>
		void construct_method(_T const* p, typename fastcall_convention<_T>::const_method fn, clone_info info) throw()
	{
		new (&this->m_strategy) method_strategy<fastcall_convention<_T>, typename clone_info::strategy>(p, fn, info);
	}
#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);
	}

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

	// constructor for const method
	template<typename _T, typename TMethod>
		SOPHIA_DELEGATE_CLASSNAME(_T const* p, TMethod fn)
	{
		this->construct_method(p, fn, clone_option_never_clone());
	}

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

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

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

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

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

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

	// constructor for const method using clone-allocator
	template<typename _T, typename TMethod, typename clone_info>
		void bind(_T const* p, TMethod fn, clone_info info)
	{
		this->strategy().object_free();
		this->construct_method(p, fn, clone_option_rebind<_T, typename clone_info::clone_allocator>(info));
	}
};

#undef SOPHIA_TEMPLATE_PARAMS
#undef SOPHIA_TEMPLATE_ARGS
#undef SOPHIA_PARAMS
#undef SOPHIA_ARGS
#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)

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