天天看點

xlua中C#調用Lua的table和function

開頭

    上一篇 文章主要介紹了xlua熱更新中生成代碼的原理,以及Lua調用C#對象的原理。Lua調用c#對象可以用兩種方式(生成代碼和反射)實作同一種結果:生成userdate儲存對象在c#側的索引,為所有成員方法生成包裹,利用Lua中已存在的其與C/C++壓棧方式的資料傳遞,進行參數和傳回值的傳遞,實作對c#對象的控制。

    那麼,在使用熱更新時,需要用Lua函數重載C#函數,這時就需要c#能夠調用到Lua。這篇文章粗粗介紹下其中的原理。不足之處,請各位多多指正。後續會加上代碼和詳細步驟解析。

正文

    上一節曾提到過Lua和C/C++的資料互動是通過棧進行的,進行資料傳遞的時候,把資料拷貝到棧上,并傳回其索引值,就可以在另一邊取到資料。而c#就可以借助這種方式,通過P/Invoke(非托管代碼調用)方式調用Lua的dll,執行Lua的C API,來完成與lua的資料通信。

    我們先看一下Lua中的資料結構:string、bool、number、table、function。string、bool和number可以在C#側找到對應的資料結構進行轉換接收,function在c#中對應着委托,table沒有對應的結構,xlua自己定義了一個LuaTable的類。在ObjectCasters.cs中,有xlua預設定義的轉換函數。

xlua中C#調用Lua的table和function
C#調用Lua table

    當給一個LuaTable(C#側)對象指派table(Lua側)時,其lua代碼相當于

CS.XXX.luatable = { a=1 }

,首先在指派時,lua這邊會把參數{a=1}壓入棧中,然後需要C#調用其生成的set包裹方法(見上一篇)進行設定。壓入之後,c#這邊就要先拿到這個table,需要先擷取類型對應的轉換器函數,将lua傳回的棧索引取到table的引用,添加到Lua系統資料庫中,然後LuaTable對象才能儲存該索引。這個索引就是table(lua)在Lua系統資料庫中的引用。LuaTable類中封裝了Get方法,其原理也是通過壓棧操作,擷取類型對應的轉換器函數,在棧上通過索引取到對應的獨享,通過此方法就可以完整的通路table的内容了。

C#調用Lua function

    如何傳遞一個function給C#調用。首先需要聲明一個委托類型(盡量使用封裝好的Action,Func),并聲明一個靜态變量。

[LuaCallCSharp]
public class TestXlua
{
    [CSharpCallLua]
    //打标簽是為了執行Generate Code後生成與委托參數比對的成員方法,并且以"__Gen_Delegate_Imp"開頭
    public delegate int Func(string s, bool b, int i);
    public static Func func;
}

//生成的方法 DelegatesGensBridge.cs
public int __Gen_Delegate_Imp11(string p0, bool p1, int p2)
{
#if THREAD_SAFE || HOTFIX_ENABLE
    lock (luaEnv.luaEnvLock)
    {
#endif
        RealStatePtr L = luaEnv.rawL;
        int errFunc = LuaAPI.pcall_prepare(L, errorFuncRef, luaReference);
        
        LuaAPI.lua_pushstring(L, p0);
        LuaAPI.lua_pushboolean(L, p1);
        LuaAPI.xlua_pushinteger(L, p2);
        
        PCall(L, 3, 1, errFunc);
 
        int __gen_ret = LuaAPI.xlua_tointeger(L, errFunc + 1);
        LuaAPI.lua_settop(L, errFunc - 1);
        return  __gen_ret;
#if THREAD_SAFE || HOTFIX_ENABLE
    }
#endif
}
           

    在将一個lua function指派給TestXlua.func時

CS.TestXlua.func = function(a,b,c) end

也跟lua table一樣,調用set包裹方法之前,把function壓入棧,通過轉換器函數拿到引用,指派給func變量。其中不同的是,lua function會在C#側建立一個委托,其過程可以簡單了解為先建立一個DelegateBridge對象,這個對象保持着lua function的索引,然後通過委托的類型,在DelegatesGensBridge生成代碼中,查找以__Gen_Delegate_Imp開頭的與委托參數一緻的方法,借此來建立一個同類型的委托,并綁定生成代碼中相比對的以__Gen_Delegate_Imp開頭的方法。通過生成函數的内部實作可以看出,其主要功能是參數壓棧和lua function的調用。

參考:

深入了解xlua基于IL代碼注入的熱更新原理

看懂xlua實作原理

繼續閱讀