HelpBuilding a wrapper for function-type variables

Posts 1–4 of 4 · Page 1 of 1
Building a wrapper for function-type variables
So as the title suggests, I'm building a wrapper for function-type variables (aka "Delegate" class), however I ran into a problem. Sometimes I need a specific calling convention, like __thiscall (or whatever), so how can I pass the calling convention as template parameter? Or what is my alternative to this?
Quote Originally Posted by R3DDOT View Post
So as the title suggests, I'm building a wrapper for function-type variables (aka "Delegate" class), however I ran into a problem. Sometimes I need a specific calling convention, like __thiscall (or whatever), so how can I pass the calling convention as template parameter? Or what is my alternative to this?
Ill get back to you about this soon, once im on my pc.
I assume it hasn't been solved yet (You haven't said so...)

Instead of implementing something like this yourself, use the <functional> header.

You can use std::function, if you want to call a member function just create a lambda or use std::bind
Code:
std::function<void()> function = [instance]( ) { instance->desired_function( [...] ); };
The contents of std::function may or may not be stored on the stack, depending on the size of the object it stores.


If you really want to implement this for passing certain things such as the calling convention, you could do something like this (Only works with function pointers):
Code:
struct tag_stdcall { };
struct tag_thiscall { };
struct tag_cdecl { };

namespace details {

	template<typename _Tag, typename _Base>
	class _Function_wrapper_base
	{
	public:
		using tag_type = _Tag;
		using self_type = _Base;
		using base_type = _Function_wrapper_base<_Tag, _Base>;

	protected:
		_Function_wrapper_base( )
			: f_( nullptr )
		{}

		_Function_wrapper_base( void *f )
			: f_( f )
		{}

		template<typename R, typename..._Args>
		_Function_wrapper_base( R(*f)( _Args... ) )
			: f_( reinterpret_cast<void*>( f ) )
		{}

		_Function_wrapper_base( std::nullptr_t )
			: f_( nullptr )
		{}

		void *__Target( ) 
		{
			return f_;
		}

		void __Target( void *f ) 
		{
			f_ = f; 
		}

		template<typename R, typename..._Args>
		void __Target( R(*f)( _Args... ) )
		{
			f_ = reinterpret_cast<void*>( f );
		}

		void __Target( std::nullptr_t )
		{ 
			f_ = nullptr;
		}

		bool __Empty( )
		{ 
			return f_ == nullptr; 
		}

	private:
		void *f_;
	};

	template<typename _Tag>
	struct is_tag : public std::false_type {};

	template<>
	struct is_tag<tag_stdcall> : public std::true_type {};

	template<>
	struct is_tag<tag_thiscall> : public std::true_type {};

	template<>
	struct is_tag<tag_cdecl> : public std::true_type {};

}

template<typename _Tag>
class function_wrapper
{
	// if _Tag is not tag_stdcall/thiscall/cdecl this will cause a compiler error
	static_assert( details::is_tag<_Tag>::value, "function_wrapper<_Tag> uses unknown _Tag type" );
};


// Template specilization, that is how we make our implementation differ per tag.
template<>
class function_wrapper<tag_stdcall> : public details::_Function_wrapper_base<tag_thiscall, function_wrapper<tag_thiscall>>
{
public:
	function_wrapper( ) 
		: base_type( ) 
	{}

	template<typename _Callable>
	function_wrapper( _Callable &&f )
		: base_type( std::forward<_Callable>( f ) )
	{}

	template<typename R = void, typename..._Args>
	R invoke( _Args&&...args )
	{
		if ( !base_type::__Empty( ) ) // Notice that we put __stdcall as a function specifier
			return (reinterpret_cast<R(__stdcall*)(_Args...)>( base_type::__Target( ) ))( std::forward<_Args>( args )... );
		throw std::exception( "function_wrapper<tag_thiscall>::invoke - empty callable object" );
	}

	void *get_target( ) 
	{ 
		return base_type::__Target( );
	}

	template<typename _Callable>
	void set_target( _Callable &&f )
	{
		base_type::__Target( std::forward<_Callable>( f ) );
	}

};

using stdcall_function = function_wrapper<tag_stdcall>;
using thiscall_function = function_wrapper<tag_thiscall>;
using cdecl_function = function_wrapper<tag_cdecl>;

// not gonna implement the rest, do them yourself!
I only wrote one template specilization, write the rest yourself (If you want more calling conventions then implement more).

Unfortunately to be able to write a function_wrapper that takes a member function you would need to write a completely different class, it's more complex, this is the gist of it:
Code:
template<typename _Ty, typename _Ret, typename..._Args>
class member_function
{
public:
	using self_type = member_function<_Ty, _Ret, _Args...>;
	using value_type = typename std::remove_all_extents<_Ty>::type;
	using ptmf_type = _Ret(value_type::*)(_Args...);

	member_function( value_type *instance, ptmf_type ptmf )
		: this_( instance ), mfn_( ptmf ), unit_( false )
	{}

	member_function( std::nullptr_t )
		: mfn_( ), this_( nullptr ), unit_( true )
	{}

	member_function( )
		: mfn_( ), this_( nullptr ), unit_( true )
	{}

	template<typename..._Ax>
	_Ret invoke( _Ax&&...args )
	{
		if ( !unit_ )
			return (this_->*mfn_)( std::forward<_Ax>( args )... );
		throw std::exception( "member_function<_Ty, _Ret, _Args...> empty function" );
	}

	void set_function( std::nullptr_t )
	{ 
		unit_ = true;
	}

	void set_function( value_type *instance, ptmf_type ptmf )
	{
		mfn_ = ptmf;
		this_ = instance;
	}


private:
	value_type *this_;
	ptmf_type mfn_;
	bool unit_;
};
Quote Originally Posted by Yemiez View Post
I assume it hasn't been solved yet (You haven't said so...)

Instead of implementing something like this yourself, use the <functional> header.

You can use std::function, if you want to call a member function just create a lambda or use std::bind
Code:
std::function<void()> function = [instance]( ) { instance->desired_function( [...] ); };
The contents of std::function may or may not be stored on the stack, depending on the size of the object it stores.


If you really want to implement this for passing certain things such as the calling convention, you could do something like this (Only works with function pointers):
Code:
struct tag_stdcall { };
struct tag_thiscall { };
struct tag_cdecl { };

namespace details {

	template<typename _Tag, typename _Base>
	class _Function_wrapper_base
	{
	public:
		using tag_type = _Tag;
		using self_type = _Base;
		using base_type = _Function_wrapper_base<_Tag, _Base>;

	protected:
		_Function_wrapper_base( )
			: f_( nullptr )
		{}

		_Function_wrapper_base( void *f )
			: f_( f )
		{}

		template<typename R, typename..._Args>
		_Function_wrapper_base( R(*f)( _Args... ) )
			: f_( reinterpret_cast<void*>( f ) )
		{}

		_Function_wrapper_base( std::nullptr_t )
			: f_( nullptr )
		{}

		void *__Target( ) 
		{
			return f_;
		}

		void __Target( void *f ) 
		{
			f_ = f; 
		}

		template<typename R, typename..._Args>
		void __Target( R(*f)( _Args... ) )
		{
			f_ = reinterpret_cast<void*>( f );
		}

		void __Target( std::nullptr_t )
		{ 
			f_ = nullptr;
		}

		bool __Empty( )
		{ 
			return f_ == nullptr; 
		}

	private:
		void *f_;
	};

	template<typename _Tag>
	struct is_tag : public std::false_type {};

	template<>
	struct is_tag<tag_stdcall> : public std::true_type {};

	template<>
	struct is_tag<tag_thiscall> : public std::true_type {};

	template<>
	struct is_tag<tag_cdecl> : public std::true_type {};

}

template<typename _Tag>
class function_wrapper
{
	// if _Tag is not tag_stdcall/thiscall/cdecl this will cause a compiler error
	static_assert( details::is_tag<_Tag>::value, "function_wrapper<_Tag> uses unknown _Tag type" );
};


// Template specilization, that is how we make our implementation differ per tag.
template<>
class function_wrapper<tag_stdcall> : public details::_Function_wrapper_base<tag_thiscall, function_wrapper<tag_thiscall>>
{
public:
	function_wrapper( ) 
		: base_type( ) 
	{}

	template<typename _Callable>
	function_wrapper( _Callable &&f )
		: base_type( std::forward<_Callable>( f ) )
	{}

	template<typename R = void, typename..._Args>
	R invoke( _Args&&...args )
	{
		if ( !base_type::__Empty( ) ) // Notice that we put __stdcall as a function specifier
			return (reinterpret_cast<R(__stdcall*)(_Args...)>( base_type::__Target( ) ))( std::forward<_Args>( args )... );
		throw std::exception( "function_wrapper<tag_thiscall>::invoke - empty callable object" );
	}

	void *get_target( ) 
	{ 
		return base_type::__Target( );
	}

	template<typename _Callable>
	void set_target( _Callable &&f )
	{
		base_type::__Target( std::forward<_Callable>( f ) );
	}

};

using stdcall_function = function_wrapper<tag_stdcall>;
using thiscall_function = function_wrapper<tag_thiscall>;
using cdecl_function = function_wrapper<tag_cdecl>;

// not gonna implement the rest, do them yourself!
I only wrote one template specilization, write the rest yourself (If you want more calling conventions then implement more).

Unfortunately to be able to write a function_wrapper that takes a member function you would need to write a completely different class, it's more complex, this is the gist of it:
Code:
template<typename _Ty, typename _Ret, typename..._Args>
class member_function
{
public:
	using self_type = member_function<_Ty, _Ret, _Args...>;
	using value_type = typename std::remove_all_extents<_Ty>::type;
	using ptmf_type = _Ret(value_type::*)(_Args...);

	member_function( value_type *instance, ptmf_type ptmf )
		: this_( instance ), mfn_( ptmf ), unit_( false )
	{}

	member_function( std::nullptr_t )
		: mfn_( ), this_( nullptr ), unit_( true )
	{}

	member_function( )
		: mfn_( ), this_( nullptr ), unit_( true )
	{}

	template<typename..._Ax>
	_Ret invoke( _Ax&&...args )
	{
		if ( !unit_ )
			return (this_->*mfn_)( std::forward<_Ax>( args )... );
		throw std::exception( "member_function<_Ty, _Ret, _Args...> empty function" );
	}

	void set_function( std::nullptr_t )
	{ 
		unit_ = true;
	}

	void set_function( value_type *instance, ptmf_type ptmf )
	{
		mfn_ = ptmf;
		this_ = instance;
	}


private:
	value_type *this_;
	ptmf_type mfn_;
	bool unit_;
};
Yeah, that helps a lot! I was stonewalled for such a long time...
Also, what's the purpose of "self_type" in the second class?
On value type you extract the class then you use it to build the definition for your "function pointer", but self_type doesn't seem to be used...

Anyway huge thanks for all of this info!
Posts 1–4 of 4 · Page 1 of 1

Post a Reply

Similar Threads

Tags for this Thread

None

Talk with us