编译时推导函数调用约定、常量性、返回值、参数等函数类型特征

已知调用约定不是类型,也无法在运行时检查。所以,看似无法根据调用约定的不同实现分治,可std::thread和std::invoke是如何处理调用约定的?

存在以下事实;语句:

(void( __stdcall* )())0 == (void( __cdecl* )())0; 

无法被正确编译,cl.exe报告的编译错误为:error C2446: “==”: 没有从“void (__cdecl *)(void)”到“void (__stdcall *)(void)”的转换 
但以下语句可以通过编译:

(void( __stdcall* )())0 == (void( __stdcall* )())0; 

即:说明编译器能根据函数类型中的调用约定不同而产生不同的函数类型,所以,可以根据函数类型的调用约定不同进行特化。(此结论不适用于VS2013,VS2013虽可根据调用约定不同产出不同函数类型,但不可利用之进行特化)

完整实现如下:

#if _MSC_VER >1800 // 大于 VS2013
	/*
		FnSynopsis
			得到函数提要,包括函数返回类型、形参数量和各参数类型、调用约定以及
		函数是否声明为CONST等。

		类型成员:
			_ResultType,函数的返回类型。
			_FormalTypes,用 tuple 表示的形式参数包,可通过 tuple_size、
		tuple_element 等类得到其中包含的形参数量或类型。
			_PtrType,复原后的函数指针类型。

		值成员:
			CallType,调用约定;参见:CallConvention
			DeclareAsConst,函数是否声明为CONST。

		示例,得到函数调用约定:

			int __stdcall global_fun( int, double );

			struct A
			{
				static char __cdecl static_member_fun( unsigned char, char *& );
				float __stdcall member_fun( wchar_t & );
				float const_member_fun( int & ) const;

				void __cdecl cdecl_ellipsis_fun( const char *, ... );
			};
			
			void cdecl_ellipsis_fun( const char *, ... );

			int main( int argc, char ** argv )
			{
				int( __stdcall* p_global_fun )(int, double)  = global_fun;
				FnSynopsis< decltype(global_fun)>::CallType;		// _STD_CALL
				FnSynopsis< remove_reference_t<decltype(*p_global_fun)>>::CallType;	// _STD_CALL

				FnSynopsis< string( int, double ) const> ::CallType;	// 默认为_CDECL_CALL,视编译设置而定
				FnSynopsis< string( int, double ) >::CallType;				// 默认为_CDECL_CALL,视编译设置而定

				FnSynopsis< decltype(cdecl_ellipsis_fun)>::CallType;	// 错误,无法处理 c-style 变参函数;参考备注。

				FnSynopsis< decltype(A::static_member_fun)>::CallType;	// _CDECL_CALL
				FnSynopsis< decltype(&A::member_fun) >::CallType;			// _STD_CALL
				FnSynopsis< decltype(&A::const_member_fun)>::CallType;	// _THIS_CALL
				FnSynopsis< decltype(&A::cdecl_ellipsis_fun)>::CallType;	// 错误,无法处理 c-style 变参函数;参考备注。

				return EXIT_SUCCESS;
			}

		备注:
			1、FnSynopsis无法处理 c-style 变参函数,如果你有此有需求,以下是建议的实现:

				template< typename _Ret, typename ..._Types >
					struct FnSynopsis< _Ret __cdecl( _Types..., ... ) >
				{
					typedef _Ret _ResultType;
					typedef std::tuple< _Types... > _FormalTypes;	// 注意:_FormalTypes只包含函数的非 ... 部分的形参描述。
					typedef _Ret( __cdecl *_PtrType )(_Types...);
					static const CallConvention CallType = _CDECL_CALL;	// 注意:c-style 变参函数的调用约定一定是 __cdecl
					enum { DeclareAsConst = false };
				};

			2、FnSynopsis无法处理未实例化的模板函数。ms c++ 标准库中 bind 函数返回的对象其 operator() 是模板函数。

		< .fuhao >
	*/	
	template< typename _Tx > struct FnSynopsis;

	enum CallConvention {
		_CDECL_CALL,	// __cdecl
		_STD_CALL,		// __stdcall
		_FAST_CALL,		// __fastcall
		_THIS_CALL,		// __thiscall
		_CLR_CALL			// __clrcall
	};

#define DEFINE_FNSYNOPSIS_IMPL( _CallType, _CallTypeValue ) \
	template< typename _Ret, typename ..._Types >\
		struct FnSynopsis< _Ret _CallType( _Types... ) >\
	{\
		typedef _Ret _ResultType;\
		typedef VOID _ClassType;\
		typedef std::tuple< _Types... > _FormalTypes;\
		typedef _Ret( _CallType *_PtrType )( _Types... );\
		static const CallConvention CallType = _CallTypeValue; \
		enum { DeclareAsConst = false };\
		enum { IsMemberFunction = false };\
	};\
	template< typename _Ret, typename ..._Types >\
		struct FnSynopsis< _Ret _CallType( _Types ... ) const >\
	{\
		typedef _Ret _ResultType;\
		typedef VOID _ClassType;\
		typedef std::tuple< _Types... > _FormalTypes;\
		typedef _Ret( _CallType *const _PtrType )( _Types... );\
		static const CallConvention CallType = _CallTypeValue; \
		enum { DeclareAsConst = true };\
		enum { IsMemberFunction = false };\
	}

#define DEFINE_MEM_FNSYNOPSIS_IMPL( _CallType, _CallTypeValue )\
	template< typename _Ret, typename _Class, typename ..._Types >\
		struct FnSynopsis< _Ret( _CallType _Class::* )(_Types...) >\
	{\
		typedef _Ret _ResultType;\
		typedef _Class _ClassType;\
		typedef std::tuple< _Types... > _FormalTypes;\
		typedef _Ret( _CallType _Class::* _PtrType) (_Types ...);\
		static const CallConvention CallType = _CallTypeValue; \
		enum { DeclareAsConst = false };\
		enum { IsMemberFunction = true };\
	};\
	template< typename _Ret, typename _Class, typename ..._Types >\
		struct FnSynopsis< _Ret( _CallType _Class::* )(_Types...) const >\
	{\
		typedef _Ret _ResultType;\
		typedef _Class _ClassType;\
		typedef std::tuple< _Types... > _FormalTypes;\
		typedef _Ret( _CallType _Class::* _PtrType) (_Types ...) const;\
		static const CallConvention CallType = _CallTypeValue; \
		enum { DeclareAsConst = true };\
		enum { IsMemberFunction = true };\
	}

	DEFINE_FNSYNOPSIS_IMPL( __cdecl, _CDECL_CALL );
	DEFINE_FNSYNOPSIS_IMPL( __stdcall, _STD_CALL );

	DEFINE_MEM_FNSYNOPSIS_IMPL( __cdecl, _CDECL_CALL );
	DEFINE_MEM_FNSYNOPSIS_IMPL( __stdcall, _STD_CALL );
	DEFINE_MEM_FNSYNOPSIS_IMPL( __thiscall, _THIS_CALL );

	#ifdef _M_CEE
	DEFINE_FNSYNOPSIS_IMPL( __clrcall, _CLR_CALL );
	DEFINE_MEM_FNSYNOPSIS_IMPL( __clrcall, _CLR_CALL );
	#else
	DEFINE_FNSYNOPSIS_IMPL( __fastcall, _FAST_CALL );
	DEFINE_MEM_FNSYNOPSIS_IMPL( __fastcall, _FAST_CALL );
	#endif

	// 针对可调用对象的 FnSynopsis,用法和 FnSynopsis 差不多。
	template< typename _Tx, typename = _Tx > struct CallableSynopsis;

	template< typename _Tx >
	struct CallableSynopsis< _Tx, typename std::enable_if< std::is_class< typename std::remove_reference< _Tx >::type >::value, _Tx >::type >
		: public FnSynopsis< decltype(&std::remove_reference<_Tx>::type::operator()) >
	{
		enum : bool { IsCallableObject = true };
	};

	template< typename _Tx >
	struct CallableSynopsis< _Tx, typename std::enable_if< !std::is_class< typename std::remove_reference< _Tx >::type  >::value, _Tx >::type >
		: public FnSynopsis< typename std::remove_pointer< typename std::remove_reference< _Tx >::type >::type >
	{
		enum : bool { IsCallableObject = false };
	};

#endif

有一点值得注意:
FnSynopsis内的_FormalTypes成员是 tuple,不是函数的参数的类型包,若需将此复原为类型包,可参考以下实现:

	void fun( int, string );

	template< typename ..._Types >
	void restore( tuple< _Types ... > && )
	{
		printf( "ArgsNumber:%d\n", sizeof...(_Types) );
	}

使用:

restore( FnSynopsis< decltype(fun) >::_FormalTypes() );

若要利用 _FormalTypes存储参数,并伺机调用,建议实现如下:

void fn1( string, int ){}

template< typename _Fn1, typename ..._Types >
auto Bypass( _Fn1 &&_Func, tuple< _Types ... > Tuple )->result_of_t< _Fn1&(_Types...)>
{
	return ApplyTupleExpand( _Func, std::move( Tuple ) );
}

template< typename _Fn1, typename ..._Types >
auto Call( _Fn1 && _Func, _Types &&... Args )->result_of_t< _Fn1&(_Types...)>
{
	FnSynopsis< remove_reference_t< remove_pointer_t< decltype(_Func)>>>::_FormalTypes Tuple = make_tuple( std::forward< _Types >( Args )... );
	return Bypass( _Func, Tuple );
}

使用:

Call( fn1, string(), 123 );

ApplyTupleExpand 实现如下:

	/*
		ApplyTupleExpand
			展开std::tuple,并将tuple元素作为参数调用_Func。

		参数:
			_Func,函数对象。ApplyTupleExpand调用此函数时会向其传递已展开为参数包的Tuple。
			Tuple,将要被展开为参数包的std::tuple对象。

		返回值:
			返回_Func的调用结果。

		注意:
			如果要处理 move 语义,需要在调用 ApplyTupleExpand 传入右值的 Tuple;

		示例:
			VOID Fun( String _RightString, String &_LRefString, CONST String &_CLRefString, String _String )
			{
			}

			template< typename _Fn1, typename ..._Types >
			VOID Call( _Fn1 &&_Func, _Types &&... Args )
			{
				tuple< _Types&&... > _Tup( std::forward< _Type >( Args )... );
				ApplyTupleExpand( _Func, std::move( _Tup ) );		注意使用std::move,否则向 Fun 函数的_RightString形参传递参数时将采用拷贝方式
			}

			int main( int argc, char ** argv )
			{
				String RightStringArg;
				String LRefStringArg;
				String LCRefStringgArg;
				String StringArg;
				Call( Fun, std::move( RightStringArg ), LRefStringArg, LCRefStringArg, StringArg );

				return NOERROR;
			}
				
		< .fuhao >
	*/
	template< std::size_t N > struct _TupleExpand{
		template< typename _Fty, typename _Ety, typename... _Remain >
		static inline auto Expand( _Fty &&_Func, _Ety && _Elem, _Remain&&... Args )->\
			decltype(_TupleExpand< N - 1 >::Expand(
			std::forward< _Fty>( _Func ), std::forward< _Ety >( _Elem ),
			std::get< N - 1 >( std::forward< _Ety >( _Elem ) ),
			std::forward< _Remain>( Args )... )){
			return _TupleExpand< N - 1 >::Expand( std::forward< _Fty >( _Func ),
				std::forward<_Ety>( _Elem ),
				std::get<N - 1>( std::forward<_Ety>( _Elem ) ),
				std::forward<_Remain>( Args )... );
		}
	};

	template<> struct _TupleExpand < 0 > {
		template< typename _Fty, typename _Ety, typename... _Remain>
		static inline auto Expand( _Fty && _Func, _Ety &&, _Remain &&... Args )->\
			decltype(::std::forward<_Fty>( _Func ) (::std::forward<_Remain>( Args )...)){
			return ::std::forward< _Fty >( _Func )(::std::forward < _Remain >( Args )...);
		}
	};

	template< typename _Fty, typename _Tuple >
	inline auto ApplyTupleExpand( _Fty && _Func, _Tuple && Tuple )->\
	decltype(_TupleExpand< std::tuple_size< typename std::decay< _Tuple >::type >::value >::Expand( std::forward< _Fty >( _Func ), std::forward< _Tuple >( Tuple ) )){
		return _TupleExpand < std::tuple_size < typename std::decay< _Tuple >::type >::value >::Expand( std::forward< _Fty>( _Func ), std::forward< _Tuple >( Tuple ) );
	}

以及一个扩展版,使用了部分 c++ 14特性的实现,如下:

#if _MSC_VER > 1800 // VS2013以上版本
	/*
		ApplyTupleExpandEx
			同 ApplyTupleExpand 的作用相同。

		参数:
			_Func,要调用的函数。
			Tuple,将要被展开为参数包的std::tuple对象。

		返回值:
			_Func 的返回值。
	*/
	template< typename _Fty, typename _Tuple, size_t ... Index >
	decltype(auto) ApplyTupleExpandImpl(
		_Fty &&_Func, _Tuple &&Tuple, std::index_sequence<Index...> )
	{
		return _Func( std::get< Index>( std::forward<_Tuple>( Tuple ) )... );
	}

	template< typename _Fty, typename _Tuple >
	decltype(auto) ApplyTupleExpandEx( _Fty &&_Func, _Tuple &&Tuple )
	{
		using std::decay;
		using std::tuple_size;
		using std::make_index_sequence;

		return ApplyTupleExpandImpl( std::forward< _Fty>( _Func ),
			std::forward< _Tuple >( Tuple ),
			make_index_sequence< tuple_size< typename decay< _Tuple>::type >::value >{} );
	}
#endif

至此,实现了一个可以推导函数调用约定的基础设施。至于作何用,基础设施的作用是无限大,发挥想像就行了。事实上,这是我的GDI对象调试器项目中一部分核心代码,用来为hook大量的GDI函数编写通用的detour函数所用。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行和易用,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值