2024年9月lua怎么调用c函数(lua怎么调用c++文件)
⑴lua怎么调用c函数(lua怎么调用c++文件)
⑵本文主要介绍lua如何调用C函数(lua如何调用C文件),下面一起看看lua如何调用C函数(lua如何调用C文件)相关资讯。
⑶XLua是腾讯的开源项目,在Unity、.和Mono。主要讨论xLua下Lua调用C#的实现原理。
⑷Lua和C#之间的数据通信机制无论是Lua调用C#还是C#调用Lua,都需要一个通信机制来完成数据传输。Lua本身是用C语言写的,所以自带了与C/C的通信机制。
⑸Lua和C/C之间的数据交互是通过栈进行的。当操作数据时,数据首先被复制到"堆栈和,然后获取数据。堆栈中的每个数据都由索引值定位。正索引值表示相对于堆栈底部的偏移索引,负索引值表示相对于堆栈顶部的偏移索引。索引值从或-开始,所以栈顶的索引值总是-,栈底的索引值总是。"堆栈和相当于Lua和C/C之间的数据中转站,每种数据都有对应的访问接口。
⑹而C#可以调用Luasdll,并执行Lua通过这个dll调用。换句话说,C#可以通过C/C的与Lua进行通信,DllImport修饰的很多数据堆栈和获取的接口都可以在xLua的LuaDLL.cs文件中找到。
⑺//LuaDLL.cs[DllImport(LUADLL,callingconvention=callingconvention。cdecl)]publicstaticexternvoidLua_pushnumber(IntPtrL,doublenumber);[DllImport(LUADLL,callingconvention=callingconvention。cdecl)]publicstaticexternvoidLua_pushboolean(IntPtrL,boolvalue);[DllImport(LUADLL,callingconvention=callingconvention。cdecl)]publicstaticexternvoidxlua_pushinteger(intptrL,intvalue);[DllImport(LUADLL,callingconvention=callingconvention。cdecl)]公共静态externdoublelua_tonumber(IntPtrL,intindex);[DllImport(LUADLL,callingconvention=callingconvention。cdecl)]publicstaticexternintxlua_tointeger(intptrL,intindex);[DllImport(LUADLL,callingconvention=callingconvention。cdecl)]publicstaticexternuintxlua_touint(intptrL,intindex);[DllImport(LUADLL,callingconvention=callingconvention。cdecl)]publicstaticexternboolLua_toboolean(intptrL,intindex);将C#对象传递给Lua对于bool和int这样的简单值类型,可以直接通过CAPI传递。但是对于C#对象,就不一样了。Lua中没有对应的类型,所以传递给Lua的只是一个C#对象的索引。具体实现请看下面的代码。
⑻//objecttranslator.cspublicvoidPush(RealStatePtrL,objecto){//...intindex=-;typetype=o.GetType;#如果!UNITY_WSA||UNITY_EDITORboolis_enum=type。IsEnumboolis_valuetype=type。IsValueType#elseboolis_enum=type。GetTypeInfo。IsEnumboolis_valuetype=type。GetTypeInfo。IsValueType#endifboolneedcache=!is_valuetype||is_enum//如果是引用或者枚举,如果(needcach:反向映射。trygetvalue(o,outindex))//如果有缓存{if(L)。uaAPI.xlua_tryget_cachedud(L,index,cacheref)==){return;}//It;这里太经典了。weaktable先删除,然后GC会延迟调用。当索引将被回收时,不注释该行将导致//collectObject(index)的重复释放;}bool是_firstinttype_id=getTypeId(L,type,outis_first);//如果类型的定义包含自己的静态readonly实例,getTypeId会推送一个实例,这个实例if(is_firstneedcach:反向映射。TryGetValue(o,outindex))){if(LuaAPI.xlua_tryget_cachedud(L,index,cacheref)==){return;}}//C#端forcachingindex=addobject(o,is_valuetype,is_enum);//将表示对象的索引推送到LuaAPI.xlua_pushsobj(l,index,type_id,Needcache,cacheref);}代码中的两个if语句主要判断缓存。如果要传递的对象已经被缓存,将直接使用缓存。如果第一次传递该对象,将执行以下两个步骤。
⑼通过addObject将对象缓存在对象池中,并获取一个索引(可以通过这个索引获取对象)。
⑽//objecttranslator.csintaddobject(objectobj,boolis_valuetype,boolis_enum){intindex=objects。add(obj);if(is_enum){enummap[obj]=index;}elseif(!is_valuetype){reversemap[obj]=index;}回报指数;}通过xlua_pushcsobj将代表对象的索引传递给Lua。
⑾参数key表示对象的索引,引用。数字meta_ref表示表示对象类型的表的索引,其值由getTypeId函数获取,后面会详细讨论。参数need_cache表示Lua端是否需要缓存,参数cache_ref表示Lua端缓存表的索引。
⑿//xlua.clua_APIvoidxlua_pushcsobj(Lua_State*L,intkey,intmeta_ref,intneed_cache,intcache_ref){int*pointer=(int*)Lua_newuserdata(L,sizeof(int));*指针=键;if(need_cache)cacheud(L,key,cache_ref);//Lua端缓存Lua_rawgeti(l,Lua_registryindex,meta_ref);lua_setmetatable(L,-);//在缓存表中为userdata设置元表}//Storekey=userdata静态Void缓存UD(Lua_State*L,Intkey,IntCache_Ref){Lua_RawGeti(L,Lua_RegistryIndex,Cache_Ref);lua_pushvalue(L,-);lua_rawseti(L,-,key);lua_pop(L,);}xlua_pushcsobj的主要逻辑是,将代表对象的索引推送给lua后,Lua会为其创建一个userdata,将userdata指向对象索引,如果需要缓存,将userdata保存在缓存表中,最后为userdata设置一个元表。也就是说,C#对象对应Lua中的一个userdata,通过使用对象索引与C#对象保持联系。
⒀在Lua为userdata设置的元表中注册C#类型信息(尤其是Lua中C#对象对应的代理userdata,后面的userdata也是这个意思,所以我赢了这里不再赘述)实际上代表了对象的类型信息。将C#对象传递给Lua后,需要告诉Lua对象的类型信息,比如对象类型有哪些成员方法、属性或者静态方法。这些都注册到Lua里之后,Lua就可以正确调用了。这个元表是由getTypeId函数生成的。
⒁//对象转换器。csinternalintgetTypeId(RealStatePtrL,Typetype,outboolis_first,LOGLEVELlog_level=LOGLEVEL。WARN){inttype_id;is_first=false如果(!typeIdMap。TryGetValue(type,outtype_id))//无引用{//...is_first=trueTypealias_type=nullaliasCfg。TryG:别名类型。全名);if(LuaAPI.lua_isnil(L,-))//还没有元,尽量使用反射元{LuaAPI.lua_pop(L,);if(TryD:alias_typ:别名类型。全名);}else{throw新Exc:无法加载type:"类型);}}//循环依赖,依赖于自己的类,比如拥有自己类型的静态readonly对象。if(typeIdMap。TryGetValue(type,outtype_id)){LuaAPI.lua_pop(L,);}else{//...LuaAPI.lua_pushvalue(L,-);type_id=LuaAPI.luaL_ref(L,LuaIndexes。LUA_注册表索引);//将LuaAPI中添加了元表。注册表中的Lua_pushnumber(l,type_id);LuaAPI.xlua_rawseti(L,-,);//元表[]=type_idLuaAPI.lua_pop(L,);如果(类型。IsValueType){typeMap。Add(type_id,type);}typeIdMap。Add(type,type_id);}}返回type_id{函数的主要逻辑是以类名为关键字,通过luaL_getmetatable获取类对应的元表。如果不可用,将由TryDelayWrapLoader函数生成。然后调用luaL_ref将元表添加到Lua注册表中,返回type_id。Type_id表示Lua注册表中元表的索引,通过该索引可以从Lua注册表中检索到元表。上面提到的xlua_pushcsobj函数就是通过使用type_id即meta_ref来获取元表,然后为userdata设置元表。
⒂让让我们看看元表是如何生成的。
⒃//objecttranslator.cspublicboolTryDelayWrapLoader(RealStatePtrL,Typetype){//...LuaAPI.luaL_newmetatable(L,type。全名);//先构建一个元表,因为加载过程中可能需要LuaAPI.lua_pop(L,);ActionRealStatePtr加载程序;inttop=LuaAPI.Lua_gettop(L);If(delaywrap。trygetvalue(type,outloader))//如果有预注册的类型元表生成器,使用{delayWrap。直接删除(类型);装载机(L);}else{#if!GEN_CODE_MINIMIZE!ENABLE_ilCPP(UNITY_EDITOR||XLUA_GENERAL)!力_反射!_STANDARD__if(!代表桥。Gen_Flag!类型。IsEnum!类型(委托)。IsAssignableFrom(类型)实用程序。IsPublic(type)){Typewrap=ce。EmitTypeWrap(类型);MethodInfo方法=wrap。getmethod("__注册",绑定Flags。静态|BindingFlags。公);方法。Invoke(null,newobject[]{L});}else{Utils。ReflectionWrap(L,type,privateAessibleFlags。包含(类型));}#elseUtils。ReflectionWrap(L,type,privateAessibleFlags。包含(类型));#endif//...}如果(顶!=LuaAPI.Lua_gettop(L)){thrownewException("顶改,之前:"top",after:"LuaAPI.Lua_gettop(L));}foreach(varnested_typeintype。getnestedtypes(bindingflags。public)){if(nested_type。isgenerictypedefinition)//筛选泛型类型定义{continue}GetTypeId(L,nested_type);}返回true}trydelaywrapploader主要用于处理两种情况
⒄判断是否有delayWrap为该类生成的代码,如果有,直接使用生成函数填充元表(loader方法)。xLua生成代码中有一个XLuaGenAutoRegister.cs文件,其中为对应的类注册了一个初始化器,这个初始化器负责将该类对应的元表生成函数添加到delayWrap中。//XLuaGenAutoRegister.cspublic类XLua_Gen_Initer_Register__{staticvoidwrapinit(LuaenvLuaenv,ObjectTranslatortranslator){//...翻译。DelayWrapLoader(typeof(TestXLua),TestXLuaWrap。__寄存器);//将类型对应的元表填充函数__Register添加到delayWrap//...}静态Voidinit(LuaenvLuaenvObjectTranslatorTranslator){Wrapinit(LuaenvTranslator);翻译。AddInterfaceBridgeCreator(typeof(System。Collections.IEnumerator)、SystemCollectionsIEnumeratorBridge。__创建);}静态XLua_Gen_Initer_Register__{XLua。Luaenv.addiniter(Init);//注册初始化器}}如果没有生成的代码,用反射(ReflectionWrap方法)填充元表,用生成的函数填充元表。以LuaCallCSharp修饰的TestXLua类为例,看看生成的函数是如何生成的。
⒅//testxLua.cs[LuaCallCSharp]公共类TestXLua{公共字符串名称;publicvoidtest(inta){}publicstaticvoidtest(inta,boolb,stringc){}}生成代码后生成的TestXLuaWrap.cs如下。
⒆公共类TestXLuaWrap{publicstaticvoid__Register(RealStatePtrL){objecttranslatortranslator=ObjectTranslatorpool。instance.Find(L);系统。typetype=typeof(testxLua);Utils。BeginObjectRegister(type,L,translator,,,,);Utils。RegisterFunc(L,Utils。MIDX方法"测试",_m_Test);Utils。RegisterFunc(L,Utils。GETTER_IDX"姓名和名称,_g_get_Name);Utils。RegisterFunc(L,Utils。塞特_IDX"姓名和名称,_s_set_Name);Utils。EndObjectRegister(type,L,translator,null,null,null,null,null);Utils。BeginClassRegister(type,L,__CreateInstance,,,);Utils。RegisterFunc(L,Utils。IDXCLS"测试",_m_test_xlua_ST_);Utils。EndClassRegister(type,L,translator);}[MonoPInvokeCallbackAttribute(typeof(LuaCSFunction))]staticint__createinstance(RealStatePtrL){try{objecttranslatortranslator=ObjectTranslatorpool。instance.Find(L);if(LuaAPI.Lua_gettop(L)==){testxLuagen_ret=newtestxLua;翻译。Push(L,gen_ret);返回;}}catch(系统。异常gen_e){returnLuaAPI.lual_error(L,"c#exception:"gen_e);}returnLuaAPI.luaL_error(L,"TestXLua构造函数的参数无效!");}[MonoPInvokeCallbackAttribute(typeof(LuaCSFunction))]staticint_m_Test(RealStatePtrL){try{objecttranslatortranslator=ObjectTranslatorPool。instance.Find(L);TestXLuagen_to_be_invoked=(TestXLua)translator。FastGetCSObj(L,);{int_a=LuaAPI.xlua_tointeger(L,);gen_to_be_调用。test(_a);返回;}}catch(系统。异常gen_e){returnLuaAPI.lual_error(L,"c#exception:"gen_e);}}[MonoPInvokeCallbackAttribute(typeof(LuaCSFunction))]staticint_m_Test_xlua_ST_(RealStatePtrL){try{{int_a=LuaAPI.xlua_tointeger(L,);bool_b=LuaAPI.lua_toboolean(L,);string_c=LuaAPI.lua_tostring(L,);TestXLua。Test(_a,_b,_c);返回;}}catch(系统。异常gen_e){returnLuaAPI.lual_error(L,"c#exception:"gen_e);}}[MonoPInvokeCallbackAttribute(typeof(LuaCSFunction))]staticint_g_get_Name(RealStatePtrL){try{objecttranslatortranslator=ObjectTranslatorpool。instance.Find(L);TestXLuagen_to_be_invoked=(TestXLua)translator。FastGetCSObj(L,);LuaAPI.lua_pushstring(L,gen_to_be_invoked。姓名);}catch(系统。异常gen_e){returnLuaAPI。luaL_error(L"c#exception:"gen_e);}返回;}[MonoPInvokeCallbackAttribute(typeof(LuaCSFunction))]staticint_s_set_Name(RealStatePtrL){try{objecttranslatortranslator=ObjectTranslatorpool。instance.Find(L);TestXLuagen_to_be_invoked=(TestXLua)translator。FastGetCSObj(L,);gen_to_be_调用。Name=LuaAPI.lua_tostring(L,);}catch(系统。异常gen_e){returnLuaAPI.lual_error(L,"c#exception:"gen_e);}返回;}}生成函数__Register主要就是这样一个框架。
⒇Utils。BeginObjectRegister,在注册非静态值(如成员变量、成员方法等)之前做一些准备工作。)的同学。主要是在元表中加入__gc和__tostring元方法,准备好方法表、getter表和setter表,可以在后面调用RegisterFunc时插入到相应的表中。
⒈//utils.cspublicstaticvoidBeginObjectRegister(TypeType,RealStatePtrL,ObjectTranslatortranslator,intmeta_count,intmethod_count,intgetter_count,intsetter_count,intType_id=-){if(Type==null){if(Type_id==-)thrownewExc:必须提供type_id");LuaAPI.xlua_rawgeti(L,LuaIndexes。LUA注册处DEX,type_id);}else{LuaAPI.lual_getmetatable(L,type。全名);//如果元表对应类型。FullName为空,创建一个新的元表并在注册表中设置if(LuapiLua_Isnil(l,-)){LuapiLua_pop(l,);LuaAPI.luaL_newmetatable(L,type。全名);}}LuaAPI.Lua_pushlightuserdata(L,LuaAPI.xlua_tag);LuaAPI.lua_pushnumber(L,);LuaAPI.lua_rawset(L,-);//设置标志if((type==null||!翻译。HasCustomOp(type))类型!=typeof(decimal)){LuaAPI.xlua_pushascistring(L,"__gc");LuaAPI.Lua_pushstdcallcfunction(L,translator.metafunctions.GCmeta);LuaAPI.lua_rawset(L,-);//为元表设置__gc方法}LuaAPI.xlua_pushhasciistring(l,"__tostring");LuaAPI.Lua_pushstdcallcfunction(L,translator.metafunctions.tostringmeta);LuaAPI.lua_rawset(L,-);//设置__tostring方法if(method_count==){LuaAPI。Lua_pushnil(l);}else{LuaAPI.lua_createtable(L,,method_count);//创建方法表}if(getter_count==){LuaAPI。Lua_pushnil(l);}else{LuaAPI.lua_createtable(L,,getter_count);//Creategettertable}if(setter_count==){luapi.Lua_pushnil(L);}else{LuaAPI.lua_createtable(L,,setter_count);//创建setter表}}多个Utils。RegisterFunc,并在不同的Lua表中注册该类的每个非静态值对应的包方法。当使用生成代码时,包装方法是动态生成的,并且为类的属性生成两个包装方法,即get和set包装方法。
⒉例如,成员方法Test对应的包方法是_m_Test,它注册在方法表中。Name变量的_g_get_Name包装方法注册在getter表中,而_s_set_Name包装方法注册在setter表中。这个被包装的方法只是对原方法的一层包装,调用这个被包装的方法本质上就是调用原方法。至于为什么需要生成包方法,后面再说。
⒊//Utils.csRegisterFunc根据宏定义的不同会有不同的版本,但类似于PublicstateptrL,Intidx,StringName,LuacsFunctionFunc){idx=ABS_idx(LuapiLua_gettop(L),idx);LuaAPI.xlua_pushaciisistring(L,name);LuaAPI.Lua_pushstdcallcfunction(L,func);LuaAPI.lua_rawset(L,idx);//添加键值对name=func}utils。EndObjectRegister指向idx指向的表,以结束该类的非静态值的注册。主要逻辑是为元表生成__index元方法和__newindex元方法,这也是Lua调用C#的核心
⒋//utils.cspublicstaticvoidEndObjectRegister(TypeType,RealStatePtrL,ObjectTranslatortranslator,LuaCSFunctioncsIndexer,LuaCSFunctioncsNewIndexer,Typebase_type,LuaCSFunctionarrayIndexer,LuaCSFunctionarrayNewIndexer){inttop=Luaapi.Lua_gettop(L);intmeta_idx=abs_idx(top,OBJ_META_IDX);intmethod_idx=abs_idx(top,METHOD_IDX);intgetter_idx=abs_idx(top,GETTER_IDX);intsetter_idx=abs_idx(top,SETTER_IDX);//beginindexgenLuaAPI.xlua_pushasciitring(L,"__指数");LuaAPI.lua_pushvalue(L,method_idx);//.按下方法表LuaAPI。Lua_pushvalue(l,getter_idx);//.在getters表中按if(csindexer==null){LuaAPI。Lua_pushnil(l);}else{//...LuaAPI.Lua_pushstdcallcfunction(L,csindexer);//.按csind:类型。basetype);//.压入BaseLuaAPI。XLUA_puhascistring(l,Luaindexfieldname);LuaAPI.lua_rawget(L,LuaIndexes。LUA_注册表索引);//.按下索引funcsif(arrayindexer==null){LuaAPI。Lua_pushnil(l);}else{//...LuaAPI.Lua_pushstdcallcfunction(L,arrayIndexer);//.PressArrayIndexer//...}LuaAPI。gen_obj_Indexer(L);//生成__index元方法if(type!=null){LuaAPI.xlua_pushaciisistring(L,LuaIndexsFieldName);LuaAPI.lua_rawget(L,LuaInd前任。LUA_注册表索引);//存储在lua索引函数表转换器中。Push(L,type);LuaAPI.lua_pushvalue(L,-);LuaAPI.lua_rawset(L,-);//registry[Luaindexes][type]=__indexfunctionLuaAPI.Lua_pop(L,);}LuaAPI.lua_rawset(L,meta_idx);//endindexgen//beginnewindexgenLuaAPI.xlua_pushascistring(L,"__newindex");LuaAPI.lua_pushvalue(L,setter_idx);if(csNewIndexer==null){LuaAPI.Lua_pushnil(L);}else{//...LuaAPI.Lua_pushstdcallcfunction(L,csN:类型。basetype);LuaAPI.xlua_pushaciitring(L,LuaNewIndexsFieldName);LuaAPI.lua_rawget(L,LuaIndexes。LUA_注册表索引);if(arrayNewIndexer==null){LuaAPI.Lua_pushnil(L);}else{//...LuaAPI.Lua_pushstdcallcfunction(L,arrayNewIndexer);//...}LuaAPI.gen_obj_newindexer(L);//生成__newindex元方法if(type!=null){LuaAPI.xlua_pushaciitring(L,LuaNewIndexsFieldName);LuaAPI.lua_rawget(L,LuaIndexes。LUA_注册表索引);//存储在luanewindexsfunc中选项表翻译器。Push(L,type);LuaAPI.lua_pushvalue(L,-);LuaAPI.lua_rawset(L,-);//registry[luanewindexes][type]=__newindex函数LuaAPI.lua_pop(L,);}LuaAPI.lua_rawset(L,meta_idx);//end新索引genLuaAPI.lua_pop(L,);}__index元方法是通过调用gen_obj_indexer获得的。在调用这个方法之前,会依次按下个参数(在代码注释中标注),在gen_obj_indexer中按下一个nil值,用来提前占用baseindex。总共有个参数将作为upvalue与闭包obj_indexer相关联。obj_indexer函数是__index元方法,其逻辑是在访问userdata[key]时,首先顺序查询RegisterFunc先前填充的methods、getters等表中是否有与该键对应的package方法,如果有可以直接使用,如果没有可以在父类中递归查找。__newindex的元方法是通过调用gen_obj_newindexer来获取的,与获取__index的原理类似,这里就不一一列举了。
⒌//xlua.clua_APIintgen_obj_indexer(Lua_State*L){Lua_pushnil(L);lua_pushlosur:方法,[]:g:csind:bas:indexfuncs,[]:arrayindexer,[]:bas:obj,[]:keyLua_APIintobj_indexer(Lua_State*L){if(!lua_isnil(L,lua_upvalueindex()){//如果方法中有键,使用methods[key]lua_pushvalue(L,);lua_gettable(L,Lua_upvalueindex());如果(!lua_isnil(L,-)){//有方法返回;}lua_pop(L,);}如果(!Lua_isnil(l,Lua_upvalueindex()){//如果键在getters中,调用getters[key]lua_pushvalue(L,);lua_gettable(L,Lua_upvalueindex());如果(!lua_isnil(L,-)){//有getterlua_pushvalue(L,);lua_call(L,,);返回;}lua_pop(L,);}如果(!Lua_isnil(l,Lua_upvalueindex())Lua_type(l,)==Lua_tnumber){//如果arrayindexer中有一个键,并且该键是一个数字,则调用ArrayIndexer[key]Lua_pushvalue(l,Lua_upvalueindex()。lua_pushvalue(L,);lua_pushvalue(L,);lua_call(L,,);返回;}如果(!Lua_isnil(l,Lua_upvalueindex()){//如果csindexer中有key,调用CSindexer[key]Lua_pushvalue(l,Lua_upvalueindex());lua_pushvalue(L,);lua_pushvalue(L,);lua_call(L,,);if(lua_toboolean(L,-)){return;}lua_pop(L,);}如果(!Lua_isnil(l,Lua_upvalueindex()){//递归查找基中的Lua_pushvalue(l,Lua_upvalueindex());而(!卢阿_伊斯尼l(L,-)){lua_pushvalue(L,-);lua_gettable(L,Lua_upvalueindex());如果(!lua_isnil(L,-))//找到{lua_replace(L,Lua_upvalueindex());//baseindex=indexfuncs[base]Lua_pop(L,);打破;}lua_pop(L,);lua_getfield(L,-,"基本类型");lua_remove(L,-);}Lua_pushnil(L);lua_replace(L,Lua_upvalueindex());//base=nil}if(!lua_isnil(L,Lua_upvalueindex()){Lua_settop(L,);lua_pushvalue(L,Lua_upvalueindex());lua_insert(L,);lua_call(L,,);//调用父类的__index,indexfuncs[base](obj,key)return;}else{return;}}Utils。BeginClassRegister,在注册类的静态值之前做一些准备工作(比如静态变量和静态方法等。).主要是为类生成对应的cls_table表,并提前创建static_getter表和static_setter表,这些表将在后面用来存储静态字段对应的get和set包方法。注意,还将为cls_table设置元表meta_table。
⒍//utils.cspublicstaticvoidBeginClassRegister(TypeType,RealStatePtrL,LuaCSFunctioncreator,intclass_field_count,intstatic_getter_count,intstatic_setter_count){objecttranslatortranslator=ObjectTranslatorPool。instance.Find(L);LuaAPI.lua_createtable(L,,class_field_计数);LuaAPI.xlua_pushascistring(L,"底层系统类型");翻译。PushAny(L,type);LuaAPI.lua_rawset(L,-);intcls_table=LuaAPI.Lua_gettop(L);SetCSTable(L,type,cls_table);LuaAPI.lua_createtable(L,,);intmeta_table=LuaAPI.Lua_gettop(L);如果(创作者!=null){LuaAPI.xlua_pushasciitring(L,"__");#ifGEN_CODE_MINIMIZEtranslator。pushcsharpwwrapper(L,creator);#elseLuaAPI.Lua_pushstdcallcfunction(L,creator);#endifLuaAPI.lua_rawset(L,-);}if(static_getter_count==){LuaAPI.Lua_pushnil(L);}else{LuaAPI.lua_createtable(L,,static_getter_count);//创建static_getter表}if(static_setter_count==){LuaAPI。Lua_pushnil(l);}else{LuaAPI.lua_createtable(L,,static_setter_count);//创建static_settertable}LuaAPI。Lua_push值(l,meta_table);LuaAPI.lua_setmetatable(L,cls_table);//设置元表}cls_table表根据类的命名空间名称逐层添加到注册表中,主要通过SetCSTable实现。
⒎//utils.cspublicstaticvoidSetCSTable(RealStatePtrL,Typetype,intcls_table){intoldtop=LuaAPI.Lua_gettop(L);cls_table=abs_idx(oldTop,cls_table);LuaAPI.xlua_pushascistring(L,LuaEnv。csharp_NAMESPACE);LuaAPI.lua_rawget(L,LuaIndexes。LUA_注册表索引);liststringpath=getpathoftype(type);//forA.B.循环处理A.B//。注册表[xlua_csharp_namespace][A]={}和注册表[xlua_csharp_namespace]//。注册表[xlua_csharp_namespace][a]。我路径。count-;I){LuaAPI.xlua_pushaciisistring(L,path[I]);如果(!=LuaAPI.xlua_pg:"err);}if(LuaAPI.lua_isnil(L,-))//如果注册表[xlua_csharp_namespace]中没有keypath[i],则添加一个键值对{LuaAPI.lua_pop(L,);LuaAPI.lua_createtable(L,,);LuaAPI.xlua_pushaciisistring(L,path[I]);LuaAPI.lua_pushvalue(L,-);LuaAPI.lua_rawset(L,-);}elseif(!LuaAPI.lua_istable(L,-)){LuaAPI.lua_settop(L,oldtop);抛出新的异常SetCSTabl:的祖先不是一个表!");}LuaAPI.lua_remove(L,-);}//处理C//registry[xlua_csharp_namespace][a][b][C]=cls_table并弹出堆栈[xlua_csharp_namespace][a][b][C]LuaAPI。xlua_pushasstring(L,path[LuaAPI.lua_pushvalue(L,cls_table);LuaAPI.lua_rawset(L,-);LuaAPI.lua_pop(L,);//添加键值对[类型对应的lua代理用户数据]=CLS_tableLuaAPI。XLUA_pushassisting(l,Luaenv。csharp_namespace)在注册表[xlua_csharp_namespace]中;LuaAPI.lua_rawget(L,LuaIndexes。LUA_注册表索引);ObjectTranslatorPool。Instance.Find(L)。PushAny(L,type);LuaAPI.lua_pushvalue(L,cls_table);LuaAPI.lua_rawset(L,-);LuaAPI.lua_pop(L,);}以A.B.C类为例,下面的表结构会添加到Luaregistry中,Luaregistry[xlua_CSharp_namespace]实际上对应的是CS全局表,所以可以直接使用CS的形式。在xLua中访问C#类时的A.B.C。
⒏Luaregistry={xlua_CSharp_namespace={-即CS全局表A={b={c=cls_table}},}多个Utils。RegisterFunc在BeginObjectRegister和EndObjectRegister之间具有与RegisterFunc相同的功能,在对应的Lua表中注册该类的每个静态值对应的包装方法。对应于静态变量的get和set包装方法将分别注册在static_getter表和static_setter中。表(只读静态变量除外)
⒐Utils。EndClassRegister,结束类的静态值的注册。类似于EndObjectRegister,但是它为cls_table的元表meta_tabl设置了__index元方法和__newindex元方法。
⒑//utils.cspublicstaticvoidEndClassRegister(TypeType,RealStatePtrL,objecttranslatortranslator){inttop=LuaAPI.Lua_gettop(L);intcls_idx=abs_idx(top,CLS_IDX);intcls_getter_idx=abs_idx(top,CLS_GETTER_IDX);intcls_setter_idx=abs_idx(top,CLS_SETTER_IDX);intcls_meta_idx=abs_idx(top,CLS_META_IDX);//beginclsindexLuaAPI.xlua_pushasciitring(L,"__指数");LuaAPI.lua_pushvalue(L,cls_getter_idx);LuaAPI.lua_pushvalue(L,cls_idx);翻译。按(L,类型。basetype);LuaAPI.xlua_pushaciisistring(L,LuaClassIndexsFieldName);LuaAPI.lua_rawget(L,LuaIndexes。LUA_注册表索引);LuaAPI.gen_cls_indexer(L);LuaAPI.xlua_pushaciisistring(L,LuaClassIndexsFieldName);LuaAPI.lua_rawget(L,LuaIndexes。LUA_注册表索引);//存储在lua索引函数表转换器中。Push(L,type);LuaAPI.lua_pushvalue(L,-);LuaAPI.lua_rawset(L,-);//注册表[LuaClassIndexs][type]=__索引函数LuaAPI.lua_pop(L,);LuaAPI.lua_rawset(L,cls_meta_idx);//endclsindex//beginclsnewindexLuaAPI.xlua_pushascistring(L,"__newindex");LuaAPI.lua_pushvalue(L,cls_setter_idx);翻译。按(L,类型。basetype);LuaAPI.xlua_pushaciisistring(L,LuaClassNewIndexsFieldName);LuaAPI.lua_rawget(L,LuaIndexes。LUA_注册表索引);LuaAPI.gen_cls_newindexer(L);LuaAPI.xlua_pushaciisistring(L,LuaClassNewIndexsFieldName);LuaAPI.lua_rawget(L,LuaIndexes。LUA_注册表索引);//存储在luanewindexs函数表转换器中。Push(L,type);LuaAPI.lua_pushvalue(L,-);LuaAPI.lua_rawset(L,-);//Registry[Luaclassisneindexs][type]=__newindex函数LuaAPI.lua_pop(L,);LuaAPI.lua_rawset(L,cls_meta_idx);//endclsnewindexLuaAPI.Lua_pop(L,);}以上六部分代码量大,逻辑复杂,这里有必要做个总结。
⒒生成的代码会为类的所有非静态值生成相应的包装方法,并将包装方法以key=func的形式注册在不同的表中。userdata元表的__index和__newindex负责从这些不同的表中找到与该键对应的包装方法,最后通过调用包装方法来控制C#对象。
⒓-lua测试代码localobj=cs.testxLuaobj.name="测试与测试-赋值操作将触发obj元表。__newindex,__newindex在setter表中查找与名称对应的setwrappingmethod_s_set_Name,然后将TestXLua对象的Name属性设置为"测试与测试通过调用_s_set_Name方法。生成的代码还将为每个类生成cls_table表,并将命名空间作为层次结构。和类的非静态值一样,生成的代码也会为类的静态值生成相应的包装方法,并注册在不同的表中(注意这里有一些区别,类的静态方直接注册在cls_table表中)。cls_table元表的__index和__newindex负责从这些不同的表中找到与键对应的包装方法,最后通过调用包装方法来控制C#类。
⒔-lua测试代码cs。TestXLua.Test-cs.testxlua获取TestXLua类对应的cls_table。由于test是一个静态方法,其对应的包method_m_Test_xlua_st_可以直接在cls_table中获取。然后通过调用_m_Test_xlua_st_间接调用TestXLua类的Test方法,用反射填充元表。当没有生成代码时,会使用反射进行注册,这和生成代码进行注册的逻辑基本相同。通过反射得到类的静态和非静态值,然后分别注册到不同的表中,填充__index和__newindex的元方法。
⒕//utils.cspublicstaticvoidreflectionwrap(RealStatePtrL,Typetype,boolprivateaessible){LuaAPI.Lua_checkstack(L,);inttop_enter=LuaAPI.Lua_gettop(L);objecttranslatortranslator=ObjectTranslatorpool。instance.Find(L);//创建obj元表LuaAPI.luaL_getmetatable(L,type。全名);if(LuaAPI.lua_isnil(L,-)){LuaAPI.lua_pop(L,);LuaAPI.luaL_newmetatable(L,type。全名);}//将xlua_tag标志LuaAPI添加到元表中。lua_pushlightuserdata(L,LuaAPI.xlua_tag);LuaAPI.lua_pushnumber(L,);LuaAPI.lua_rawset(L,-);//元表[xlua_tag]=intobj_meta=LuaAPI。Lua_gettop(l);LuaAPI.Lua_newtable(L);intcls_meta=LuaAPI.Lua_gettop(L);LuaAPI.Lua_newtable(L);intobj_field=LuaAPI.Lua_gettop(L);LuaAPI.Lua_newtable(L);intobj_getter=LuaAPI.Lua_gettop(L);LuaAPI.Lua_newtable(L);intobj_setter=LuaAPI.Lua_gettop(L);LuaAPI.Lua_newtable(L);intcls_field=LuaAPI.Lua_gettop(L);//将cls_field设置为命名空间SetCSTable(L,type,cls_field);//完成将cls_field设置到命名空间LuaAPI.Lua_newtable(L);intcls_getter=LuaAPI.Lua_gettop(L);LuaAPI.Lua_newtable(L);intcls_setter=LuaAPI.Lua_gettop(L);LuaCSFunctionitem_getterLuaCSFunctionitem_settermakeflectionwrap(L,type,cls_field,cls_getter,cls_setter,obj_field,obj_getter,obj_setter,obj_meta,outitem_getter,outitem_setter,privateA:装订标志。公);//初始化objmetatableLuaAPI.xlua_pushascistring(L,"__gc");LuaAPI.Lua_pushstdcallcfunction(L,translator.metafunctions.GCmeta);LuaAPI.lua_rawset(L,obj_meta);LuaAPI.xlua_pushascistring(L,"__tostring");LuaAPI.Lua_pushstdcallcfunction(L,translator.metafunctions.tostringmeta);LuaAPI.lua_rawset(L,obj_meta);LuaAPI.xlua_pushascistring(L,"__指数");LuaAPI.lua_pushvalue(L,obj_field);//.upvaluemethods=obj_fieldLuaAPI.Lua_pushvalue(L,obj_getter);//.upvaluegetters=obj_gettertranslator。PushFixCSFunction(L,item_getter);//.upvaluecsindexer=item_gettertranslator。PushAny(L,type。basetype);//按BaseType,。upvaluebaseluaapi.xlua_puhascistring(l,luandexfieldname);LuaAPI.lua_rawget(L,LuaIndexes。LUA_注册表索引);//.upvalueindexfuncs=registry[luindexxs]Luapis.Lua_pushnil(l);//.upvaluearrayindexer=nilLuaAPI.gen_obj_indexer(L);//生成__index函数//存储在Luaindexes函数tableslaapi.xlua_pushacistring(l,Luaindexfieldname);LuaAPI.lua_rawget(L,LuaInd前任。LUA_注册表索引);翻译。Push(L,type);//按typeluapi.lua_pushvalue(l,-);LuaAPI.lua_rawset(L,-);//registry[Luaindexes][type]=__indexfunctionLuaAPI.Lua_pop(L,);LuaAPI.lua_rawset(L,obj_meta);//set__index表示obj_meta["__指数"]=生成的__index函数luapi.xlua_pushastring(l,"__newindex");LuaAPI.lua_pushvalue(L,obj_setter);翻译。PushFixCSFunction(L,item_setter);翻译。按(L,类型。basetype);LuaAPI.xlua_pushaciitring(L,LuaNewIndexsFieldName);LuaAPI.lua_rawget(L,LuaIndexes。LUA_注册表索引);LuaAPI.Lua_pushnil(L);LuaAPI.gen_obj_newindexer(L);//存储在luanewindexs函数表LuaAPI.xlua_pushasciitring(L,LuaNewIndexsFieldName);LuaAPI.lua_rawget(L,LuaIndexes。LUA_注册表索引);翻译。Push(L,type);LuaAPI.lua_pushvalue(L,-);LuaAPI.lua_rawset(L,-);//registry[luanewindexes][type]=__newindex函数LuaAPI.lua_pop(L,);LuaAPI.lua_rawset(L,obj_meta);//set__newindex//finishinitobj元表LuaAPI.xlua_pushaciitring(L,"底层系统temType");翻译。PushAny(L,type);LuaAPI.lua_rawset(L,cls_field);//cls_field["底层系统类型"]=type,如果(type!=空类型。IsEnum){LuaAPI.xlua_pushascistring(L,"__CastFrom");翻译。PushFixCSFunction(L,genEnumCastFrom(type));LuaAPI.lua_rawset(L,cls_field);}//init类metaLuaAPI.xlua_pushaciitring(L,"__指数");LuaAPI.lua_pushvalue(L,cls_getter);LuaAPI.lua_pushvalue(L,cls_field);翻译。按(L,类型。basetype);LuaAPI.xlua_pushaciisistring(L,LuaClassIndexsFieldName);LuaAPI.lua_rawget(L,LuaIndexes。LUA_注册表索引);LuaAPI.gen_cls_indexer(L);//存储在luaindexs函数表LuaAPI.xlua_pushasciitring(L,LuaClassIndexsFieldName);LuaAPI.lua_rawget(L,LuaIndexes。LUA_注册表索引);翻译。Push(L,type);LuaAPI.lua_pushvalue(L,-);LuaAPI.lua_rawset(L,-);//registry[LuaClassIndexs][type]=__index函数LuaAPI.lua_pop(L,);LuaAPI.lua_rawset(L,cls_meta);//set__indexLuaAPI.xlua_pushaciistring(L,"__newindex");LuaAPI.lua_pushvalue(L,cls_setter);翻译。按(L,类型。basetype);LuaAPI.xlua_pushaciisistring(L,LuaClassNewIndexsFieldName);LuaAPI.lua_rawget(L,LuaIndexes。LUA_注册表索引);LuaAPI.gen_cls_newindexer(L);//存储在luanewindexs函数表LuaAPI.xlua_pushasciitring(L,LuaClassNewIndexsFieldName);LuaAPI.lua_rawget(L,LuaIndexes。LUA_注册表索引);翻译。Push(L,type);LuaAPI.lua_pushvalue(L,-);LuaAPI.lua_rawset(L,-);////Registry[Luaclassisneindexs][type]=__newindex函数LuaAPI.lua_pop(L,);LuaAPI.lua_rawset(L,cls_meta);//set__newindex//...}调用C#方法时,参数的传递要先解决上一个遗留的问题。为什么需要为类的静态或非静态值生成相应的包装方法?其实就是用包装的方法来处理参数传递的问题。
⒖为了与Lua正确通信,C函数定义了协议。这个协议定义了参数和返回值传递方法:C函数通过Lua中的堆栈接受参数,参数按正序堆栈(第一个参数先堆栈)。因此,当函数启动时,lua_gettop(L)可以返回函数接收的参数个数。第一个参数(如果有)在索引处,最后一个参数在索引lua_gettop(L)处。当需要向Lua返回值时,C函数只需要将它们按正序推送到堆栈上(先推第一个返回值),然后返回这些返回值的个数。这些返回值下的栈上的任何东西都会被Lua扔掉。和Lua函数一样,从Lua调用C函数可以有很多返回值。
⒗也就是说,Lua调用C函数时,参数会自动堆栈,Lua内部已经实现了这个机制。文章开头也提到了C#可以通过C/C的与Lua进行通信,所以C#需要Lua传递的参数是通过CAPI获取的,这个逻辑封装在包装方法中。以TestXLua以Test方法为例,它需要一个int参数。所以它的包装方法需要通过CAPI获取一个int参数,然后用这个参数调用真正的方法。
⒘[MonoPInvokeCallbackAttribute(typeof(LuaCSFunction))]staticint_m_Test(RealStatePtrL){try{objecttranslatortranslator=ObjectTranslatorpool。instance.Find(L);TestXLuagen_to_be_invoked=(TestXLua)translator。FastGetCSObj(L,);{int_a=LuaAPI.xlua_tointeger(L,);//获取int参数gen_to_be_invoked。test(_a);//调用realTest方法返回;}}catch(系统。异常gen_e){returnLuaAPI.lual_error(L,"c#exception:"gen_e);}}这也解释了为什么需要为类的属性生成相应的get和set方法,因为只有当Lua的访问或赋值操作转换成函数调用的形式时,才能利用函数调用机制自动将参数堆栈起来,传递给C#
⒙-lua测试代码对象。name="测试与测试-赋值操作设置器["姓名和名称]("测试与测试)-函数调用形式这里我提一下函数重载的问题。因为C#支持重载,所以会有多个同名的函数,但是参数不一样。在这种情况下,我们只能通过调用同名函数时传递的参数来判断应该调用哪个函数。
⒚[Luacallcsharp]publilasstestxLua{//functionoverloadsTestpublicvoidTest(inta){}//functionoverloadsTestpublicvoidTest(boolb){}//为Test生成的包装方法[monopinvokekallbackattribute(类型为(LuacsFunction))。]staticint_m_Test(RealStatePtrL){try{objecttranslatortranslator=ObjectTranslatorpool。instance.Find(L);TestXLuagen_to_be_invoked=(TestXLua)translator。FastGetCSObj(L,);intgen_param_count=LuaAPI.Lua_gettop(L);If(gen_param_count==Lua类型。Lua_tnumber==LuaAPI。Lua_type(l,))//根据参数的个数和类型确定调用哪个方法{int_a=LuaAPI。XLUA_toInteger(l,);gen_to_be_调用。test(_a);返回;}if(gen_param_count==Lua类型。Lua_tboolean==LuaAPI。Lua_type(l,))//根据参数的个数和类型确定调用哪个方法{bool_b=LuaAPI。Lua_tobboolean(l,);gen_to_be_调用。test(_b);返回;}}catch(系统。异常gen_e){returnLuaAPI.lual_error(L,"c#exception:"gen_e);}returnLuaAPI.luaL_error(L,"TestXLua的参数无效。Test!");G#和Lua都有自动垃圾收集机制,彼此都不知道。如果传递给Lua的C#对象被C#自动回收,而Lua在不知情的情况下继续使用,必然会导致不可预知的错误。所以基本原理是传递给Lua的C#对象不能自动回收,只有Lua在确定不再使用后才能通知C#回收。
⒛为了保证C#不会自动回收对象,所有传递给Lua的对象都会保持被对象引用。传递给Lua的真实对象索引是objects中对象的索引。
Lua中为对象索引建立的userdata会存储在缓存表中,缓存表的引用模式设置为弱引用。使用
//objecttranslator.csluaapi.Lua_newtable(L);//创建缓存表LuaAPI.Lua_newtable(L);//创建元表luaapi。xlua_pushascristing(l"__模式");LuaAPI.xlua_pushascistring(L,"v");LuaAPI.lua_rawset(L,-);//元表[__mode]=v,表示该表中的所有值都是对Luapis的弱引用。Lua_setMetatable(l,-);//设置元表cacheref=LuaAPI。Lua_ref(l,Lua索引。Lua_Registry索引);当这个userdata在Lua中不再被引用时,会从缓存表中移除,在LuaGC中回收,回收前会调用userdata元表的__gc方法,以通知c#"我不在Lua中不再使用这个对象,所以它是你回收的时候了。在BeginObjectRegister方法内部,将__gc方法添加到userdata的元表中。
//Utils.csBeginObjectRegister方法if((type==null||!翻译。HasCustomOp(type))类型!=typeof(decimal)){LuaAPI.xlua_pushascistring(L,"__gc");LuaAPI.Lua_pushstdcallcfunction(L,translator.metafunctions.GCmeta);LuaAPI.lua_rawset(L,-);//为元表}转换器设置__gc方法。元功能。GCMeta实际上是StaticLuaCallbacks的LuaGC方法。
//staticluaallbacks.cs[MonoPInvokeCallback(typeof(LuaCSFunction))]publicstaticintLuaGC(RealStatePtrL){try{intudata=LuaAPI.xlua_tocsobj_safe(L,);如果(udata!=-){Obobjecttranslatortranslator=objecttranslatorpool。instance.Find(L);如果(译者!=null){translator.collectobject(udata);}}返回;}catch(Exceptione){returnLuaAPI.lual_error(L,"luagch:"e);}}LuaGC方再次调用collectObject方法。该对象将从collectObject方法内的对象中移除,因此该对象不再被固定引用,并且可以由C#GC正常回收。
//objecttranslator.csinternalvoidcollectobject(intobj_index_to_collect){objecto;如果(对象。trygetvalue(obj_index_to_collect,outo)){objects。remove(obj_index_to_collect);如果(o!=null){intobj_index;//luagc是在调用__gc之前移除弱表,期间可能会再次将同一个对象推送给lua,关联新的ind:反向映射。TryGetValue(o,outobj_index))obj_index==obj_index_to_collect){if(is_enum){enummap。移除(o);}else{reverseMap。移除(o);}}}}}参考带中文注释的xLua源代码,在Lua中注册C#类。之后Lua注册表的模拟结构可以理解Xlua实现原理的标签: