Lua中動態型別的實現
最近在閱讀《Lua的設計與實現》
裡面講到了很多Lua語言的實現細節。我也寫寫文章,以加強印象。
Lua是一種動態型別語言。所以要入手Lua原始碼,從型別定義開始確實是一個不錯的切入點。
C++中的union結構可以在同一塊記憶體當中存取不同型別的值或結構,Lua在型別實現當中大量使用了該結構。
首先在lua。h中我們可以看到Lua對所有基本型別的定義。
除了空型別LUA_TNONE之外的基本型別有:
空型別、布林型、指標、數字、字串、表、函式、UserData、協程
在lobject。h中我們可以看到所有型別的定義。
在Lua中根據資料型別是否需要進行垃圾回收將資料分為了GCObject與其他資料。
在lobject。h中我們可以看到這樣的資料結構
我們可以看到Value型別中有一個GCObject的指標,其實這個物件就包含了所有需要進行垃圾回收的物件。
至於另外幾個我們可以看到
一、*p就指的是輕量級的指標、
二、n的luaNumber透過查詢定義就可以看到其實它就是一個double型別。
在luaconf。h中可以將double改成其他的資料型別以滿足我們的需要。
三、b的int則代表了lua中的整型。
這樣我們就可以透過Value來表示GCObject和所有不需要進行垃圾回收的資料型別。但是GCObject需要繼續細分,我們將會看到GCObject也是一個聯合體。
接下來我們就來看GCObject裡面包含了什麼。
在lstate。h中包含了GCObject的定義。
暫時先不管GCObject中的GCheader到底是什麼,我們就先看下面的幾個資料型別吧。
我們可以看到下面的所有資料型別都是lua的基本型別,包含了字串、UserData、閉包、表等等。這些都是在Lua中需要進行垃圾回收的物件。
透過union我們可以用這種資料結構來表示所有這些型別。
那麼GCheader是什麼呢?
GCHeader其實就包含了一個CommonHeader,那這個CommonHeader是什麼?
其實這指代了垃圾回收物件當中所有需要的資料,包括GCObject指標、資料型別、以及垃圾回收標誌。
在閱讀程式碼的過程中我們也會發現在字串、Table等型別中都是以CommonHeader開頭的。
因為所有的GC物件都以CommonHeader開頭,所以某種程度上,這些所有型別都繼承自CommonHeader。
所以在GCObject中的gch是為了無差別地對待所有的GC物件,也就是所有GC物件的父類(雖然在這裡並沒有面向物件的概念,但是從實現上來說是一樣的)
到現在為止,所有的物件已經能夠被統一的資料結構——Value表示出來了,但是Value並不是自省的,也就是它並不知道自己是什麼型別的,所以最終Lua又最後封裝了一層,
也就是在lobject。h中封裝的TValue:
value代表了值,而tt則是表示其型別,幫助lua識別資料是何種型別。
Lua的通用資料組織結構總結:
上方的資料結構被下方的資料結構所包含。
CommonHead:
//用於表示GC物件的通用資料
GCObject *next//下一個指向的GC物件
lu_byte tt//具體型別
lu_byte marked//GC標誌代表是否需要被回收
GCHeader:
//用於表示GC物件的統一資料結構,便於查詢
CommonHeader
GCObject:
//GC物件聯合體,用於表示任何一個需要進行垃圾回收的物件
GCheader gch//可以表示任何一種GC物件型別
TString ts//字串
UData u//UserData
Closure cl//閉包
Table h//表
Proto p//存放函式原型的資料結構
UpVal uv//上值
lua_State th//虛擬機器/協程
Value:
//GC物件與基本物件聯合體,用於表示所有的lua基本型別
GCObject *gc//GC回收物件
void *p//指標
lua_Number n//浮點數,預設為double
int b//整型
TValue:
//自省的Lua資料型別
Value value//任意基本型別
int tt//型別識別符號
實際在lua內部還需要根據不同型別進行不同操作,雖然在使用lua的時候是不需要知道其型別的,值得型別會隨著賦值而進行變換,但是在內部操作的時候還是需要轉成特定的型別。
畢竟C不是動態型別的。
所以在lobject。h中定義了每種型別設值得型別轉換方法。
內部大量的資料轉換操作讓lua在使用中做到了可以將任意型別塞入到同一個變數當中。
也促成了動態型別語言的優勢。
總的來說Lua的資料結構大體就是這樣。