我們都知道Lua是一門(mén)動(dòng)態(tài)類型的腳本語(yǔ)言,也就是說(shuō)同一個(gè)變量可以在不同的時(shí)刻指向不同類型的數(shù)據(jù)挥等。例如
local a = nil
a = 1
a = "123"
而在Lua中有8中基礎(chǔ)的數(shù)據(jù)類型:nil(空)捕仔,boolean(布爾),number(數(shù)字)歧杏,string(字符串),table(表)迷守,function(函數(shù))犬绒,userdata(自定義類型),thread(協(xié)程)兑凿,那這幾種基礎(chǔ)類型在Lua中是怎么定義的凯力,而Lua又是怎么實(shí)現(xiàn)動(dòng)態(tài)類型的呢?
一礼华、C語(yǔ)言中實(shí)現(xiàn)通用數(shù)據(jù)結(jié)構(gòu)的設(shè)想
Lua是使用C語(yǔ)言實(shí)現(xiàn)的一種腳本語(yǔ)言咐鹤,那么我們?nèi)绻覀兿朐贑語(yǔ)言中實(shí)現(xiàn)一種新的通用的數(shù)據(jù)類型一般會(huì)怎么去做呢?
定義一個(gè)結(jié)構(gòu)體圣絮,這個(gè)結(jié)構(gòu)體中一定要有一個(gè)字段來(lái)存儲(chǔ)當(dāng)前結(jié)構(gòu)體所表示的數(shù)據(jù)類型祈惶,同時(shí)需要一些字段來(lái)存儲(chǔ)不同數(shù)據(jù)類型的具體數(shù)據(jù)
二、Lua中通用數(shù)據(jù)結(jié)構(gòu)的實(shí)現(xiàn)
2.1基本數(shù)據(jù)類型宏的定義
先看一下Lua中定義的幾種基本數(shù)據(jù)類型的宏:
//(lua.h)
/*
** basic types
*/
#define LUA_TNONE (-1)
#define LUA_TNIL 0
#define LUA_TBOOLEAN 1
#define LUA_TLIGHTUSERDATA 2
#define LUA_TNUMBER 3
#define LUA_TSTRING 4
#define LUA_TTABLE 5
#define LUA_TFUNCTION 6
#define LUA_TUSERDATA 7
#define LUA_TTHREAD 8
這些宏對(duì)應(yīng)的數(shù)據(jù)類型如下表:
lua_Number對(duì)應(yīng)的C語(yǔ)言的基本數(shù)據(jù)類型double扮匠,所以Lua中的number類型表示的都是實(shí)數(shù)(雙精度浮點(diǎn)數(shù))捧请,Lua中沒(méi)有整數(shù)類型。
//(luaconf.h)
/*
** {==================================================================
@@ LUA_NUMBER is the type of numbers in Lua.
** CHANGE the following definitions only if you want to build Lua
** with a number type different from double. You may also need to
** change lua_number2int & lua_number2integer.
** ===================================================================
*/
#define LUA_NUMBER_DOUBLE
#define LUA_NUMBER double
//(lua.h)
/* type of numbers in Lua */
typedef LUA_NUMBER lua_Number;
LUA_TLIGHTUSERDATA和LUA_TUSERDATA都是void *(指針類型)棒搜。根據(jù)名字我們知道他們對(duì)應(yīng)的是Lua的userdata基本數(shù)據(jù)類型疹蛉,但是兩者是有一些區(qū)別的。LUA_TLIGHTUSERDATA表示那些內(nèi)存分配與釋放都是由Lua外部的使用者要管理的對(duì)象力麸,而LUA_TUSERDATA表示的都是通過(guò)Lua來(lái)管理生命周期的對(duì)象可款,也就是LUA_TUSERDATA指向的對(duì)象是需要加入到Lua的GC(Garbage Collection 垃圾回收)中的育韩。
2.2需要GC的基本數(shù)據(jù)類型
我們知道Lua有自己的GC機(jī)制,那么哪些基礎(chǔ)數(shù)據(jù)類型需要加到GC中闺鲸,哪些又不需要筋讨,怎么區(qū)分呢?
在Lua中用一個(gè)宏來(lái)表示哪些數(shù)據(jù)類型需要進(jìn)行GC操作:
//(lobject.h)
#define ttype(o) ((o)->tt)
#define iscollectable(o) (ttype(o) >= LUA_TSTRING)
所以說(shuō)LUA_TSTRING之前的數(shù)據(jù)類型是都不需要GC摸恍,也就是string版仔,table,function误墓,userdata蛮粮,thread都需要GC的。
在Lua中需要進(jìn)行GC操作的數(shù)據(jù)類型都會(huì)有個(gè)CommonHeader宏定義的成員谜慌,并且這個(gè)成員在定義的最開(kāi)始部分然想。
/*
** Common Header for all collectable objects (in macro form, to be
** included in other objects)
*/
#define CommonHeader GCObject *next; lu_byte tt; lu_byte marked
/*
** Common header in struct form
*/
typedef struct GCheader {
CommonHeader;
} GCheader;
next : 指向下一個(gè)GC鏈表的成員
tt : 表示數(shù)據(jù)的類型,即前面的那些表示數(shù)據(jù)類型的宏
marked :GC時(shí)欣范,相關(guān)的標(biāo)記位变泄。
到了這里我們可以使用一個(gè)共同體(union),將所有需要進(jìn)行GC的數(shù)據(jù)類型囊括起來(lái):
//(lstate.h)
/*
** Union of all collectable objects
*/
union GCObject {
GCheader gch;
union TString ts;
union Udata u;
union Closure cl;
struct Table h;
struct Proto p;
struct UpVal uv;
struct lua_State th; /* thread */
};
所以GCObject可以表示Lua中所有需要GC的數(shù)據(jù)類型。
2.3Lua中所有數(shù)據(jù)類型表示
既然所有需要GC的數(shù)據(jù)類型使用GCObject表示恼琼,那么同理所有的數(shù)據(jù)類型也可以用一個(gè)共同體表示:
//(lobject.h)
/*
** Union of all Lua values
*/
typedef union {
GCObject *gc;
void *p;
lua_Number n;
int b;
} Value;
結(jié)合我們(一)中所說(shuō)的妨蛹,我們現(xiàn)在有了可以表示所有類型數(shù)據(jù)的Value了,那么就還需要一個(gè)表示數(shù)據(jù)類型的字段晴竞,所以Lua中給我們定義了一個(gè)TValue的結(jié)構(gòu)體:
//(lobject.h)
/*
** Tagged Values
*/
#define TValuefields Value value; int tt
typedef struct lua_TValue {
TValuefields;
} TValue;
從這個(gè)結(jié)構(gòu)體我們可以看到使用int類型的tt字段表示當(dāng)前的數(shù)據(jù)類型蛙卤,使用Value來(lái)表示任意類型的值。這樣TValue就可以表示Lua中任意的數(shù)據(jù)類型了噩死。
我們用一個(gè)圖來(lái)表示一下Lua通用的數(shù)據(jù)結(jié)構(gòu)的組織:
三颤难、通用類型與具體類型轉(zhuǎn)換
3.1判斷是否是具體的數(shù)據(jù)類型
//(lobject.h)
/* Macros to test type */
#define ttype(o) ((o)->tt)
#define ttisnil(o) (ttype(o) == LUA_TNIL)
#define ttisnumber(o) (ttype(o) == LUA_TNUMBER)
#define ttisstring(o) (ttype(o) == LUA_TSTRING)
#define ttistable(o) (ttype(o) == LUA_TTABLE)
#define ttisfunction(o) (ttype(o) == LUA_TFUNCTION)
#define ttisboolean(o) (ttype(o) == LUA_TBOOLEAN)
#define ttisuserdata(o) (ttype(o) == LUA_TUSERDATA)
#define ttisthread(o) (ttype(o) == LUA_TTHREAD)
#define ttislightuserdata(o) (ttype(o) == LUA_TLIGHTUSERDATA)
o為通用數(shù)據(jù)類型TValue,tt則為T(mén)Value結(jié)構(gòu)體中表示具體數(shù)據(jù)類型的字段。
3.2獲得具體數(shù)據(jù)類型的值
//(lobject.h)
#define gcvalue(o) check_exp(iscollectable(o), (o)->value.gc)
#define pvalue(o) check_exp(ttislightuserdata(o), (o)->value.p)
#define nvalue(o) check_exp(ttisnumber(o), (o)->value.n)
#define rawtsvalue(o) check_exp(ttisstring(o), &(o)->value.gc->ts)
#define tsvalue(o) (&rawtsvalue(o)->tsv)
#define rawuvalue(o) check_exp(ttisuserdata(o), &(o)->value.gc->u)
#define uvalue(o) (&rawuvalue(o)->uv)
#define clvalue(o) check_exp(ttisfunction(o), &(o)->value.gc->cl)
#define hvalue(o) check_exp(ttistable(o), &(o)->value.gc->h)
#define bvalue(o) check_exp(ttisboolean(o), (o)->value.b)
#define thvalue(o) check_exp(ttisthread(o), &(o)->value.gc->th)
#define l_isfalse(o) (ttisnil(o) || (ttisboolean(o) && bvalue(o) == 0))
3.3設(shè)置具體數(shù)據(jù)類型的值
//(lobject.h)
/* Macros to set values */
#define setnilvalue(obj) ((obj)->tt=LUA_TNIL)
#define setnvalue(obj,x) \
{ TValue *i_o=(obj); i_o->value.n=(x); i_o->tt=LUA_TNUMBER; }
#define setpvalue(obj,x) \
{ TValue *i_o=(obj); i_o->value.p=(x); i_o->tt=LUA_TLIGHTUSERDATA; }
#define setbvalue(obj,x) \
{ TValue *i_o=(obj); i_o->value.b=(x); i_o->tt=LUA_TBOOLEAN; }
#define setsvalue(L,obj,x) \
{ TValue *i_o=(obj); \
i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TSTRING; \
checkliveness(G(L),i_o); }
#define setuvalue(L,obj,x) \
{ TValue *i_o=(obj); \
i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TUSERDATA; \
checkliveness(G(L),i_o); }
#define setthvalue(L,obj,x) \
{ TValue *i_o=(obj); \
i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TTHREAD; \
checkliveness(G(L),i_o); }
#define setclvalue(L,obj,x) \
{ TValue *i_o=(obj); \
i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TFUNCTION; \
checkliveness(G(L),i_o); }
#define sethvalue(L,obj,x) \
{ TValue *i_o=(obj); \
i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TTABLE; \
checkliveness(G(L),i_o); }
#define setptvalue(L,obj,x) \
{ TValue *i_o=(obj); \
i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TPROTO; \
checkliveness(G(L),i_o); }
#define setobj(L,obj1,obj2) \
{ const TValue *o2=(obj2); TValue *o1=(obj1); \
o1->value = o2->value; o1->tt=o2->tt; \
checkliveness(G(L),o1); }