lua與C交互:基於棧操作,lua調用C函數時,需要寫個封裝函數,從棧上取出調用參數,調用C函數后把結果放到棧上;C要調用lua函數,也把參數放到棧上,用luaAPI完成調用后,從棧上取出結果。
Xlua lua調用C#
1. 沒有生成靜態代碼,反射調用
a.把C#對象映射到lua的userdata,userdata只保留一個信息,就是這個對象在C#側的objects_pool的索引
b.根據obj獲取其ClassType,根據type與調用函數(位置棧里邊index2),通過反射獲取實際執行的MethodInfo
c.根據MethodInfo構造一個滿足LuaCSFunction的delegate並壓棧
d.delegate構造,根據MethodInfo,取得args信息,根據args從棧中取出賦值,反射調用函數后把結果壓棧;GetAsType是把棧上Lua對象轉換成指定類型的C#對象,PushCsObject是把一個C#對象按映射規則壓到Lua棧上
e.將統一的反射調用方法objectIndex設置為所有C#對象的metatable的__index
lua_call_Csharp示例:
[MonoPInvokeCallback(typeof(LuaCSFunction))]
public static int objectIndex(RealStatePtr L)
{
object obj = objects_pool[GetCSObjectId(L, 1)];
Type objType = obj.GetType();
string index = LuaAPI.lua_tostring(L, 2);
MethodInfo method = objType.GetMethod(index);
PushCSFunction(L, (IL) =>
{
ParameterInfo[] parameters = method.GetParameters();
object[] args = new object[parameters.Length];
for(int i = 0; i < parameters.Length; i++)
{
args[i] = GetAsType(IL, i + 2, parameters[i].ParameterType);
}
object ret = method.Invoke(obj, args);
PushCSObject(IL, ret);
return 1;
});
return 1;
}
Csharp_call_lua示例:
public object[] Call(params object[] args)
{
int old_top = LuaAPI.lua_gettop(L);
LuaAPI.lua_getref(L, func_ref);
for(int i = 0; i < args.Length; i++)
{
Type arg_type = args[i].GetType();
if (arg_type == typeof(double))
{
LuaAPI.lua_pushnumber(L, (double)args[i]);
}
// ... other c# type
}
LuaAPI.lua_call(L, args.Length, -1);
object[] ret = new object[LuaAPI.lua_gettop(L) - old_top];
for(int i = 0; i < ret.Length; i++)
{
int idx = old_top + i + 1;
if(LuaAPI.lua_isnumber(L, idx))
{
ret[i] = LuaAPI.lua_tonumber(L, idx);
}
// ... other lua type
}
return ret;
}
2. 非反射,靜態生成代碼
反射帶來的問題:a.拆裝箱開銷 b.stripping引用的反射失效 c.泛型方法,觸發JIT,引起IOS異常 d.失去靜態檢查好處
非反射做法:用反射的方式,用工具生成要用在lua側調用的類的,類似反射調用代碼(見lua_call_Csharp示例),只是去除了里邊的反射內容,因為針對每個類是確定的,不需要用反射
靜態代碼的問題:
a.生成時,有宏定義的方法,如Editor會有問題
b.生成的代碼,增大編譯后的代碼大小,尤其il2cpp,相應地增加包體大小