本文共 2931 字,大约阅读时间需要 9 分钟。
C++对象模型(上)
要实现完善的C++反射机制,我们需要深入了解C++的对象模型。但是很遗憾,C++标准并没有明确规定C++对象模型,而是把这个自由度交给了编译器,不同的编译器下产生的对象并不一定是二进制兼容的。不过比较幸运的是主流的编译器实现的方式都大同小异,而且,我们很多时候并不一定需要深入到编译结果,通过宏和模板就能解决大部分的问题。
一、全局函数
全局函数(或者静态函数)是比较容易通过模板获取到函数类型信息的,通过一个模板函数就可以: template< typename RetType, typename... Param > inline void CreateGlobalFunWrap(RetType ( *pFun )( Param... ), const char* szType, const char* szName) { IFunctionWrap* pWrap = TFunctionWrap<RetType, Param...>::GetInst(); STypeInfoArray InfoArray = MakeFunArg<RetType, Param...>(); CScriptBase::RegisterGlobalFunction(pWrap, (uintptr_t)pFun, InfoArray, szType, szName); } #define REGIST_GLOBALFUNCTION_IMPLEMENT( _fun_type, _function, _fun_name_lua ) \ XS::SGlobalExe _fun_name_lua##_register( ( XS::CreateGlobalFunWrap( \ (_fun_type)(&_function), NULL, #_fun_name_lua ), true ) );二、类成员函数
类成员函数相对于全局函数,仅仅多了一个类对象的指针,我们可以通过定义一个局部静态函数将对类成员函数的调用转化为对静态函数的调用: #define REGIST_CLASSFUNCTION_IMPLEMENT( _function_type, _function, _function_name ) \ _function_name##_Base_Class; \ namespace _function_name##_namespace \ { \ template<typename _RetType, typename... Param> \ struct TFunctionRegister \ { \ typedef decltype ( (_function_type)nullptr ) _fun_type;\ static _RetType Call( org_class* pThis, Param ... p ) \ { \ _fun_type funMember = (_fun_type)&org_class::_function; \ return (pThis->*funMember)(p...); \ }\ static void Register()\ { \ XS::CreateClassFunWrap( &TFunctionRegister::Call, #_function_name );\ } \ }; \ \ template<typename ClassType, typename RetType, typename... Param> \ TFunctionRegister<RetType, Param...> Decl( RetType ( ClassType::*pFun )( Param... ) );\ template<typename ClassType, typename RetType, typename... Param> \ TFunctionRegister<RetType, Param...> Decl( RetType ( ClassType::*pFun )( Param... ) const );\ typedef decltype( Decl( (_function_type)nullptr ) ) RegisterImpl;\ \ static XS::CScriptRegisterNode RegisterNode( listRegister, &RegisterImpl::Register ); \ }\三、类成员变量
类的成员变量也可以通过定义局部静态函数,将对成员变量的访问转换为对函数的调用,但是我们这里使用了另外一种方法,求出成员变量相对于类对象的地址偏移,对内存进行直接读写: #define REGIST_CLASSMEMBER_GETSET_IMPLEMENT( _member, _new_name, _enableGetter, _enableSetter ) \ _new_name##_Base_Class; \ namespace _new_name##_namespace \ { \ static void Register()\ { \ org_class* c = (org_class*)0x4000000; \ IFunctionWrap* funGetSet[2];\ funGetSet[0] = _enableGetter ? XS::CreateMemberGetWrap( &c->_member ) : NULL;\ funGetSet[1] = _enableSetter ? XS::CreateMemberSetWrap( &c->_member ) : NULL;\ ptrdiff_t offset = ((ptrdiff_t)&c->_member) - (ptrdiff_t)c;\ XS::CScriptBase::RegisterClassMember( funGetSet, offset,\ XS::MakeMemberArg( c, &c->_member ), #_new_name );\ } \ static XS::CScriptRegisterNode RegisterNode( listRegister, &Register ); \ }; \ typedef _new_name##_Base_Class到这里为止,我们已经成功的获取到函数以及变量的类型信息,并且将对应的访问函数在系统中进行注册,这些功能已经能满足很大一部分对C++反射的需求了,但是我们仍然要继续往前走,下一节将会通过反射实现可以在脚本中运行时从C++类派生出脚本类,并且在脚本的派生类中可以对C++基类的虚函数进行重载,那么当这个脚本类实例的基类指针在C++中被调用时,将会准确的调用到脚本类对应的函数,也就是脚本和C++之间无缝衔接的多态。
未完待续,完整源码:
转载地址:http://lgqci.baihongyu.com/