如何构建个人网站,安阳贴吧论坛,wordpress搬家后访问异常,网站后台登陆不进去xlua源码分析#xff08;五#xff09; struct类型优化 上一节我们分析了xlua是如何实现lua层访问C#值类型的#xff0c;其中我们重点提到了xlua默认实现方式下#xff0c;struct访问的效率问题。实际上#xff0c;xlua还提供了两种优化的方式#xff0c;可以大大提高str… xlua源码分析五 struct类型优化 上一节我们分析了xlua是如何实现lua层访问C#值类型的其中我们重点提到了xlua默认实现方式下struct访问的效率问题。实际上xlua还提供了两种优化的方式可以大大提高struct访问的性能。具体例子在Examples 12_ReImplementInLua中。
第一种优化方式就是在lua层改造C#的structC# struct push到lua层时仍为userdata但它的metatable不指向C#层struct而是lua层自己实现的
function test_vector3(title, v1, v2)print(title)v1.x 100print(v1.x, v1.y, v1.z)print(v1, v2)print(v1 v2)v1:Set(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z)print(v1)print(CS.UnityEngine.Vector3.Normalize(v1))
endlocal get_x, set_x xlua.genaccessor(0, 8)
local get_y, set_y xlua.genaccessor(4, 8)
local get_z, set_z xlua.genaccessor(8, 8)local fields_getters {x get_x, y get_y, z get_z
}
local fields_setters {x set_x, y set_y, z set_z
}local ins_methods {Set function(o, x, y, z)set_x(o, x)set_y(o, y)set_z(o, z)end
}local mt {__index function(o, k)--print(__index, k)if ins_methods[k] then return ins_methods[k] endreturn fields_getters[k] and fields_getters[k](o)end,__newindex function(o, k, v)if fields_setters[k] then fields_setters[k](o, v) else error(no such field .. k) endend,__tostring function(o)return string.format(vector3 { %f, %f, %f}, o.x, o.y, o.z)end,__add function(a, b)return CS.UnityEngine.Vector3(a.x b.x, a.y b.y, a.z b.z)end
}xlua.setmetatable(CS.UnityEngine.Vector3, mt)
test_vector3(----after change metatable----, CS.UnityEngine.Vector3(1, 2, 3), CS.UnityEngine.Vector3(7, 8, 9))这里的代码就是在lua层实现了一下Vector3的get/set属性和方法然后替换掉原先的metatablexlua.setmetatable就是做这个工作的替换的逻辑很简单就是找到要替换类的type id重新设置到registry表里
public static int XLuaMetatableOperation(RealStatePtr L)
{try{ObjectTranslator translator ObjectTranslatorPool.Instance.Find(L);Type type getType(L, translator, 1);if (type null){return LuaAPI.luaL_error(L, xlua.metatable_operation, can not find c# type);}bool is_first false;int type_id translator.getTypeId(L, type, out is_first);var param_num LuaAPI.lua_gettop(L);if (param_num 1) //get{LuaAPI.xlua_rawgeti(L, LuaIndexes.LUA_REGISTRYINDEX, type_id);return 1;}else if (param_num 2) //set{if (LuaAPI.lua_type(L, 2) ! LuaTypes.LUA_TTABLE){return LuaAPI.luaL_error(L, argument #2 must be a table);}LuaAPI.lua_pushnumber(L, type_id);LuaAPI.xlua_rawseti(L, 2, 1);LuaAPI.xlua_rawseti(L, LuaIndexes.LUA_REGISTRYINDEX, type_id);return 0;}else{return LuaAPI.luaL_error(L, invalid argument num for xlua.metatable_operation: param_num);}}catch (Exception e){return LuaAPI.luaL_error(L, c# exception in xlua.metatable_operation: e);}
}不过lua层的Vector3依旧是userdata如何在lua层对userdata设置/获取数据呢为此xlua提供了xlua.genaccessor函数它接受两个参数第一个参数表示要设置/获取的字段相对于struct的内存偏移第二个参数表示要设置/获取的字段类型对于Vector3xyz的偏移分别为048而它们的类型均为floatfloat在xlua预先定义的类型ID为8
#define T_INT8 0
#define T_UINT8 1
#define T_INT16 2
#define T_UINT16 3
#define T_INT32 4
#define T_UINT32 5
#define T_INT64 6
#define T_UINT64 7
#define T_FLOAT 8
#define T_DOUBLE 9genaccessor函数是在C层实现的那其实很简单了就是把userdata作为要访问内存的首地址加上偏移量offset执行memcpy即可如果是get就是从userdata拷贝到value再push到lua栈如果是set就先从lua栈上取出value再拷贝到userdata。
#define DIRECT_ACCESS(type, push_func, to_func) \
int xlua_struct_get_##type(lua_State *L) {\CSharpStruct *css (CSharpStruct *)lua_touserdata(L, 1);\int offset xlua_tointeger(L, lua_upvalueindex(1));\type val;\if (css NULL || css-fake_id ! -1 || css-len offset sizeof(type)) {\return luaL_error(L, invalid c# struct!);\} else {\memcpy(val, ((css-data[0]) offset), sizeof(type));\push_func(L, val);\return 1;\}\
}\
\
int xlua_struct_set_##type(lua_State *L) { \CSharpStruct *css (CSharpStruct *)lua_touserdata(L, 1);\int offset xlua_tointeger(L, lua_upvalueindex(1));\type val;\if (css NULL || css-fake_id ! -1 || css-len offset sizeof(type)) {\return luaL_error(L, invalid c# struct!);\} else {\val (type)to_func(L, 2);\memcpy(((css-data[0]) offset), val, sizeof(type));\return 0;\}\
}\上面例子的运行结果如下 第二种优化方式是将struct映射成table即C#层push到lua层的struct不再为userdata而是一个tablexlua提供了PackAsTable这个attribute指示生成代码时采用映射table的方式
[GCOptimize(OptimizeFlag.PackAsTable)]
public struct PushAsTableStruct
{public int x;public int y;
}然后lua层也需要实现配套的代码即struct的object metatable和class metatable相当于在lua层实现struct
local mt {__index {SwapXY function(o) --成员函数o.x, o.y o.y, o.xend},__tostring function(o) --打印格式化函数return string.format(struct { %d, %d}, o.x, o.y)end,
}xlua.setmetatable(CS.XLuaTest.PushAsTableStruct, mt)local PushAsTableStruct {Print function(o) --静态函数print(o.x, o.y)end
}setmetatable(PushAsTableStruct, {__call function(_, x, y) --构造函数return setmetatable({x x, y y}, mt)end
})xlua.setclass(CS.XLuaTest, PushAsTableStruct, PushAsTableStruct)在测试代码中我们先在C#层push一下struct
PushAsTableStruct test;
test.x 100;
test.y 200;
luaenv.Global.Set(from_cs, test);然后再在lua层进行测试
print(--------------from csharp---------------------)
assert(type(from_cs) table)
print(from_cs)
CS.XLuaTest.PushAsTableStruct.Print(from_cs)
from_cs:SwapXY()
print(from_cs)print(--------------from lua---------------------)
local from_lua CS.XLuaTest.PushAsTableStruct(4, 5)
assert(type(from_lua) table)
print(from_lua)
CS.XLuaTest.PushAsTableStruct.Print(from_lua)
from_lua:SwapXY()
print(from_lua)此时C#层push时不会再生成userdata而是生成一个table然后设置字段x和字段y
public void PushXLuaTestPushAsTableStruct(RealStatePtr L, XLuaTest.PushAsTableStruct val)
{if (XLuaTestPushAsTableStruct_TypeID -1){bool is_first;XLuaTestPushAsTableStruct_TypeID getTypeId(L, typeof(XLuaTest.PushAsTableStruct), out is_first);}LuaAPI.xlua_pushcstable(L, 2, XLuaTestPushAsTableStruct_TypeID);LuaAPI.xlua_pushasciistring(L, x);LuaAPI.xlua_pushinteger(L, val.x);LuaAPI.lua_rawset(L, -3);LuaAPI.xlua_pushasciistring(L, y);LuaAPI.xlua_pushinteger(L, val.y);LuaAPI.lua_rawset(L, -3);}同样的道理要从lua层把struct传递到C#层就要获取lua层的table把它的字段x和字段y取出依次赋值到C#对象上
public static void UnPack(ObjectTranslator translator, RealStatePtr L, int idx, out XLuaTest.PushAsTableStruct val)
{val new XLuaTest.PushAsTableStruct();int top LuaAPI.lua_gettop(L);if (Utils.LoadField(L, idx, x)){translator.Get(L, top 1, out val.x);}LuaAPI.lua_pop(L, 1);if (Utils.LoadField(L, idx, y)){translator.Get(L, top 1, out val.y);}LuaAPI.lua_pop(L, 1);}例子的输出结果如下 这两种优化方式各有优劣第一种方式userdata比table更加省内存而第二种方式使用原始table操作性能上要比使用userdata要好。两种方式都需要额外生成一些代码。与tolua相比tolua的struct是采用了类似第二种的方式tolua的struct在lua层就是个table需要完整按照C#层实现一遍struct。而数据传输的逻辑稍微不太相同tolua是使用lua函数进行数据传输例如Vector3tolua可以通过一个get函数直接返回3个float*给C#层也可以通过一个new函数直接使用xyz三个参数构造出一个lua层的structpack和unpack的逻辑都放在了lua层里。
function Vector3.New(x, y, z) local t {x x or 0, y y or 0, z z or 0}setmetatable(t, Vector3) return t
endfunction Vector3.Get(v) return v.x, v.y, v.z
end