tolua#代碼簡要分析

簡介 編程

tolua#是Unity靜態綁定lua的一個解決方案,它經過C#提供的反射信息分析代碼並生成包裝的類。它是一個用來簡化在C#中集成lua的插件,能夠自動生成用於在lua中訪問Unity的綁定代碼,並把C#中的常量、變量、函數、屬性、類以及枚舉暴露給lua。它是從cstolua衍變而來。從它的名字能夠看出,它是集成了原來的tolua代碼經過二次封裝寫了一個C#與tolua(c)的一箇中間層。windows

All problems in computer science can be solved by another level of indirection, except of course for the problem of too many indirections閉包

基礎 框架

要想了解tolua#是如何集成的咱們須要對C#的一些特性有一些瞭解,好比了解C#與原生代碼交互的方式等。,咱們假設讀者已經對lua和tolua++有一個比較熟悉,咱們略過lua與c或者C++的交互方式,主要介紹一些C#的特性,來幫助咱們接下來分析tolua#的集成原理。編輯器

C#特性 函數

Attribute 工具

Attribute 是一種可由用戶自由定義的修飾符(Modifier),能夠用來修飾各類須要被修飾的目標。特性Attribute 的做用是添加元數據。元數據能夠被工具支持,好比:編譯器用元數據來輔助編譯,調試器用元數據來調試程序。Unity以及tolua#中就會用Attribute來輔助作一些事情。oop

值類型與引用類型 lua

只因此要提這兩個概念,是由於很好得理解這兩個概念有助於咱們寫出比較高效的C#代碼。spa

咱們知道,C#中的每一種類型要麼是值類型,要麼是引用類型。因此每一個對象要麼是值類型的實例,要麼是引用類型的實例。

引用類型和值類型都繼承自System.Object類。不一樣的是,幾乎全部的引用類型都直接從System.Object繼承,而值類型則繼承其子類,即直接繼承System.ValueType。

做爲全部類型的基類,System.Object提供了一組方法,這些方法在全部類型中都能找到,其中包含toString方法及clone等方法。

System.ValueType直接繼承System.Object,即System.ValueType自己是一個類類型,而不是值類型;System.ValueType沒有添加任何成員,但覆蓋了所繼承的一些方法,使其更適合於值類型。例如,ValueType重寫了Equals()方法,從而對值類型按照實例的值來比較,而不是引用地址來比較。

簡單瞭解了值類型與引用類型那麼咱們下面來看下C#中的裝箱和拆箱的概念。

裝箱和拆箱

裝箱和拆箱是值類型和引用類型之間相互轉換是要執行的操做。

1.    裝箱在值類型向引用類型轉換時發生

2.    拆箱在引用類型向值類型轉換時發生

1 object objValue = 4;
2 
3 int value = (int)objValue;

  

上面的兩行代碼會執行一次裝箱操做將整形數字常量4裝箱成引用類型object變量objValue;而後又執行一次拆箱操做,將存儲到堆上的引用變量objValue存儲到局部整形值類型變量value中。

一樣咱們須要看下IL代碼:

 1 .locals init (
 2 
 3 [0] object objValue,
 4 
 5 [1] int32 'value'
 6 
 7 ) //上面IL聲明兩個局部變量object類型的objValue和int32類型的value變量
 8 
 9 IL_0000: nop
10 
11 IL_0001: ldc.i4.4 //將整型數字4壓入棧
12 
13 IL_0002: box [mscorlib]System.Int32 //執行IL box指令,在內存堆中申請System.Int32類型須要的堆空間
14 
15 IL_0007: stloc.0 //彈出堆棧上的變量,將它存儲到索引爲0的局部變量中
16 
17 IL_0008: ldloc.0//將索引爲0的局部變量(即objValue變量)壓入棧
18 
19 IL_0009: unbox.any [mscorlib]System.Int32 //執行IL 拆箱指令unbox.any 將引用類型object轉換成System.Int32類型
20 
21 IL_000e: stloc.1 //將棧上的數據存儲到索引爲1的局部變量即value

 

拆箱操做的執行過程和裝箱操做過程正好相反,是將存儲在堆上的引用類型值轉換爲值類型並給值類型變量。

C#調用原生代碼

由於tolua#底層庫是使用的tolua(c語言編寫),那麼就須要經過C#來調用原生代碼,咱們從LuaDll.cs中摘取一段代碼來演示如何從C#中調用原生代碼。

1 Public class LuaDll
2 
3 {
4 
5   [DllImport(LUADLL, CallingConvention = CallingConvention.Cdecl)]
6 
7   public static extern void lua_close(IntPtr luaState);
8 
9 }

 

其中LUADLL對應的字符串就是tolua,在不一樣的平臺上mono會去加載對應的tolua.dll或者tolua.so等文件並調用對應的函數。具體能夠參考mono官方的教程。

tolua#集成

tolua#集成主要分爲兩部分,一部分是運行時須要的代碼包括一些手寫的和自動生成的綁定代碼,另外一部分是編輯器相關代碼,主要提供代碼生成、編譯lua文件等操做,具體就是Unity編輯器中提供的功能。用mono打開整個tolua#的工程,文件結構大致以下所示:

 

Runtime

    Source

        Generate 這個文件裏面是生成的綁定代碼

        LuaConst.cs 這個文件是一些lua路徑等配置文件。

    ToLua

        BaseLua 一些基礎類型的綁定代碼

        Core 提供的一些核心功能,包括封裝的LuaFunction LuaTable LuaThread LuaState LuaEvent、調用tolua原生代碼等等。

        Examples 示例

        Misc 雜項,目前有LuaClient LuaCoroutine(協程) LuaLooper(用於tick) LuaResLoader(用於加載lua文件)

        Reflection 反射相關

Editor

    Editor

        Custom

            CustomSettings.cs 自定義配置文件,用於定義哪些類做爲靜態類型、哪些類須要導出、哪些附加委託須要導出等。

    ToLua

        Editor

            Extend 擴展一些類的方法。

            ToLuaExport.cs 真正生成lua綁定的代碼

            ToLuaMenu.cs Lua菜單上功能對應的代碼

            ToLuaTree.cs 輔助樹結構

Generate All 流程

瞭解了tolua#的大體文件結構,下面咱們來看下tolua#的Generate All 這個功能來分析下它的生成過程。生成綁定代碼主要放在ToLuaExport.cs裏面,咱們並不會對每個函數進行細緻的講解,若是有什麼不瞭解的地方,能夠直接看它的代碼。

GenLuaDelegates函數

生成委託綁定的代碼,它會從CustomSettings.customDelegateList裏面取出全部自定義導出的委託列表,而後把CustomSettings.customTypeList裏面的全部類型中的委託根據必定規則加入到list中,最後調用ToLuaExport.GenDelegates()方法來生成委託綁定的代碼,生成的代碼在DelegateFactory.cs文件中。

因爲該函數比較簡單,咱們這裏不作展開,能夠直接查看ToLuaExport.cs中的GenDelegates()並配合DelegateFactory.cs來查看。

GenerateClassWraps 函數

遍歷全部須要導出的類,而後調用ToLuaExport.Generate()方法來生成類的綁定代碼。

下面咱們來看下ToLuaExport.Generate()方法,其基本流程以下所示:

從上面的流程圖咱們能夠看到,整個過程仍是比較清楚的,若是這個類是枚舉類型,那麼它會調用枚舉導出的接口,而若是這個類型是一個普通的類,那麼它就會調用上圖所示的相應的流程將代碼導出。至於結構體類型,目前應該是隻支持一些特定的結構體,須要在lua中對應一份實現(Assets\ToLua\Lua目錄中),固然它生成的代碼也有一些依賴於tolua#的核心運行時,咱們前面簡單的講解了如何在編輯器中生成綁定代碼,接下來咱們講一下它的核心運行時。

tolua#的核心運行時

tolua#的運行代碼包含SourceàGenerate下面的綁定代碼以及ToLuaàBaseType代碼以及Core下面的核心代碼。接下來咱們着重講一下Core下面的幾個主要類。

LuaAttribute.cs

咱們前面基礎知識部分已經講過,它在tolua#生成綁定代碼時作一些標示使用。

LuaBaseRef.cs

Lua中對象對應C#中對象的一個基類,主要做用是有一個reference指向lua裏面的對象,引用計數判斷兩個對象是否相等等。

好比LuaFunction裏面的reference是指向lua裏面的一個閉包的,而LuaTable的reference是指向lua中的一個table的。

LuaDll.cs

這個類的主要做用就是實現了C#調用原生代碼的功能,其中的原理咱們也在上面的基礎章節提到了如何在C#中調用原生代碼,這裏咱們就不展開去講了。

LuaState.cs

這裏面是對真正的lua_State的封裝,包括初始化lua路徑,加載相應的lua文件,註冊咱們前面生成的綁定代碼以及各類輔助函數。

ObjectTranslator.cs

接下來,咱們着重說一下這個ObjectTranslator這個類,這個類代碼很少,它存在的主要意義就是給lua中對C#對象的交互提供了基礎,簡單來講就是C#中的對象在傳給lua時並非直接把對象暴露給了lua,而是在這個OjbectTranslator裏面註冊並返回一個索引(能夠理解爲windows編程中的句柄),並把這個索引包裝成一個userdata傳遞給lua,而且設置元表。具體能夠查看tolua_pushnewudata代碼,以下所示:

 1 // tolua# 代碼
 2 
 3 static void PushUserData(IntPtr L, object o, int reference)
 4 
 5 {
 6 
 7   int index;
 8 
 9   ObjectTranslator translator = ObjectTranslator.Get(L);
10 
11   if (translator.Getudata(o, out index))
12 
13   {
14 
15   if (LuaDLL.tolua_pushudata(L, index))
16 
17   {
18 
19   return;
20 
21   }
22 
23   translator.Destroyudata(index);
24 
25   }
26 
27   index = translator.AddObject(o);
28 
29   LuaDLL.tolua_pushnewudata(L, reference, index);
30 
31 }
32 
33 // tolua++ 代碼 
34 
35 LUALIB_API void tolua_pushnewudata(lua_State *L, int metaRef, int index)
36 
37 {
38 
39     lua_getref(L, LUA_RIDX_UBOX);
40 
41     tolua_newudata(L, index);
42 
43     lua_getref(L, metaRef);
44 
45     lua_setmetatable(L, -2);
46 
47     lua_pushvalue(L, -1);
48 
49     lua_rawseti(L, -3, index);
50 
51     lua_remove(L, -2);    
52 
53 }

 

 

而在lua須要經過上面傳到lua裏面的對象調用C#的方法時,它會調用ToLua.CheckObject或者ToLua.ToObject從ObjectTranslator獲取真正的C#對象。下面咱們把ToLua.ToObject的代碼作個示例:

 1 public static object ToObject(IntPtr L, int stackPos)
 2 
 3 {
 4 
 5   int udata = LuaDLL.tolua_rawnetobj(L, stackPos);
 6 
 7   if (udata != -1)
 8 
 9   {
10 
11   ObjectTranslator translator = ObjectTranslator.Get(L);
12 
13   return translator.GetObject(udata);
14 
15   }
16 
17   return null;
18 
19 }

 

總結

經過對tolua#的簡單分析,咱們對tolua#是怎麼實現lua與Unity交互有了一個基礎的認識,若是想對tolua#有一個比較深刻的瞭解,那麼就須要咱們仔細去研究代碼、示例以及用它來實際地去作些東西。

由於看的時間不是不少,因此理解上不免有錯誤,若是發現問題還請指正。前段時間也完整實現了一套虛幻4中的使用lua框架,但願有時間的話也能跟你們分享一下,固然若是你有興趣瞭解,也能夠留言,這樣我會盡可能抽時間來把實現的具體細節跟你們分享一下。

相關文章
相關標籤/搜索