在上一篇里,我們知道一個(gè)Lua的對(duì)象包含對(duì)象的值和對(duì)象的類型信息主卫√幽回顧一下:
typedef struct lua_TValue {
Value value_;
int tt_;
}TValue;
其中,tt_
是tag type的簡(jiǎn)寫簇搅,一共包含三個(gè)部分完域,分別是:
- bit位0-3表示大類型
- bit位4-5表示小類型
- bit位6表示是否可以垃圾回收
顯然,既然Lua的所有類型統(tǒng)一用TValue表示瘩将,TValue就應(yīng)該具備一些基本的操作吟税,用來(lái)存取類型示例的基本屬性。從Lua的源碼來(lái)看姿现,可以把TValue的基本操作歸結(jié)如下:
- 判斷對(duì)象的具體類型
- 獲取對(duì)象的值
- 設(shè)置對(duì)象的值
如果用ADT來(lái)表示肠仪,可以寫成:
TValue:
get_type()
get_value()
set_value()
我們逐個(gè)來(lái)看。
get type
Lua里备典,用了一組宏來(lái)判斷TValue的具體類型异旧,首先是四個(gè)萃取tt_
信息的宏:
/* raw type tag of a TValue */
#define rttype(o) ((o)->tt_)
/* tag with no variants (bits 0-3) */
#define novariant(x) ((x) & 0x0F)
/* type tag of a TValue (bits 0-3 for tags + variant bits 4-5) */
#define ttype(o) (rttype(o) & 0x3F)
/* type tag of a TValue with no variants (bits 0-3) */
#define ttnov(o) (novariant(rttype(o)))
其中norariant是直接操作tt_
字段的,其他三個(gè)參數(shù)都是TValue對(duì)象提佣,我們可以去掉novariant:
#define rttype(o) ((o)->tt_)
#define ttype(o) (rttype(o) & 0x3F)
#define ttnov(o) (rttype(o) & 0x0F)
進(jìn)一步吮蛹,還可以把rttype展開(kāi):
#define rttype(o) ((o)->tt_)
#define ttype(o) ((o)->tt_ & 0x3F)
#define ttnov(o) ((o)->tt_ & 0x0F)
Lua并沒(méi)有針對(duì)tt_三個(gè)部分的萃取都單獨(dú)提供宏,萃取4-5bit以及萃取第6個(gè)bit由于后面各只被用到一次拌屏,所以不必單獨(dú)提供一個(gè)宏匹涮。當(dāng)然,如果單獨(dú)提供槐壳,則會(huì)讓api看上去更完備和對(duì)稱然低。
進(jìn)一步,Lua提供了兩個(gè)基本的check:
#define checktag(o,t) (rttype(o) == (t))
#define checktype(o,t) (ttnov(o) == (t))
現(xiàn)在务唐,就可以對(duì)每種TValue類型做判斷:
/* Macros to test type */
#define ttisnumber(o) checktype((o), LUA_TNUMBER)
#define ttisfloat(o) checktag((o), LUA_TNUMFLT)
#define ttisinteger(o) checktag((o), LUA_TNUMINT)
#define ttisnil(o) checktag((o), LUA_TNIL)
#define ttisboolean(o) checktag((o), LUA_TBOOLEAN)
#define ttislightuserdata(o) checktag((o), LUA_TLIGHTUSERDATA)
#define ttisstring(o) checktype((o), LUA_TSTRING)
#define ttisshrstring(o) checktag((o), ctb(LUA_TSHRSTR))
#define ttislngstring(o) checktag((o), ctb(LUA_TLNGSTR))
#define ttistable(o) checktag((o), ctb(LUA_TTABLE))
#define ttisfunction(o) checktype(o, LUA_TFUNCTION)
#define ttisclosure(o) ((rttype(o) & 0x1F) == LUA_TFUNCTION)
#define ttisCclosure(o) checktag((o), ctb(LUA_TCCL))
#define ttisLclosure(o) checktag((o), ctb(LUA_TLCL))
#define ttislcf(o) checktag((o), LUA_TLCF)
#define ttisfulluserdata(o) checktag((o), ctb(LUA_TUSERDATA))
#define ttisthread(o) checktag((o), ctb(LUA_TTHREAD))
#define ttisdeadkey(o) checktag((o), LUA_TDEADKEY)
實(shí)際上雳攘,我們可以針對(duì)上一篇的類型列表做一個(gè)縮進(jìn):
#define ttisnil(o) checktag((o), LUA_TNIL)
#define ttislightuserdata(o) checktag((o), LUA_TLIGHTUSERDATA)
#define ttisboolean(o) checktag((o), LUA_TBOOLEAN)
#define ttisnumber(o) checktype((o), LUA_TNUMBER) // 1
#define ttisfloat(o) checktag((o), LUA_TNUMFLT)
#define ttisinteger(o) checktag((o), LUA_TNUMINT)
#define ttisfunction(o) checktype(o, LUA_TFUNCTION) // 2
#define ttislcf(o) checktag((o), LUA_TLCF)
#define ttisclosure(o) ((rttype(o) & 0x1F) == LUA_TFUNCTION) // **4**
#define ttisCclosure(o) checktag((o), ctb(LUA_TCCL))
#define ttisLclosure(o) checktag((o), ctb(LUA_TLCL))
#define ttisstring(o) checktype((o), LUA_TSTRING) // 3
#define ttisshrstring(o) checktag((o), ctb(LUA_TSHRSTR))
#define ttislngstring(o) checktag((o), ctb(LUA_TLNGSTR))
#define ttisfulluserdata(o) checktag((o), ctb(LUA_TUSERDATA))
#define ttistable(o) checktag((o), ctb(LUA_TTABLE))
#define ttisthread(o) checktag((o), ctb(LUA_TTHREAD))
#define ttisdeadkey(o) checktag((o), LUA_TDEADKEY)
可以看到,1,2,3三個(gè)地方判斷大類型枫笛,用的是checktype吨灭,也就是只需判斷0-3bit即可;而4這個(gè)地方判斷則使用的是4-5bit判斷小類型刑巧。其余的地方使用的是0-6bit喧兄;不過(guò)還差一點(diǎn)无畔,那就是cbt
宏。展開(kāi)看一下:
/* Bit mark for collectable types */
#define BIT_ISCOLLECTABLE (1 << 6)
/* mark a tag as collectable */
#define ctb(t) ((t) | BIT_ISCOLLECTABLE)
可見(jiàn)吠冤,ctb
宏的作用是設(shè)置6個(gè)bit浑彰,表示這是一個(gè)可垃圾回收類型。此外拯辙,針對(duì)GCObject郭变,Lua還提供了一個(gè)判斷TValue里的tag type和GCObject里的tag type是否一致的判斷:
/* Macros for internal tests */
#define righttt(obj) (ttype(obj) == gcvalue(obj)->tt)
get value
獲取無(wú)類型信息的Value:
#define val_(o) ((o)->value_)
進(jìn)一步,根據(jù)上一節(jié)的類型判斷涯保,就可以實(shí)現(xiàn)帶類型校驗(yàn)的值獲取方法诉濒,依然是一組宏:
/* Macros to access values */
#define ivalue(o) check_exp(ttisinteger(o), val_(o).i)
#define fltvalue(o) check_exp(ttisfloat(o), val_(o).n)
#define nvalue(o) check_exp(ttisnumber(o), \
(ttisinteger(o) ? cast_num(ivalue(o)) : fltvalue(o)))
#define gcvalue(o) check_exp(iscollectable(o), val_(o).gc)
#define pvalue(o) check_exp(ttislightuserdata(o), val_(o).p)
#define tsvalue(o) check_exp(ttisstring(o), gco2ts(val_(o).gc))
#define uvalue(o) check_exp(ttisfulluserdata(o), gco2u(val_(o).gc))
#define clvalue(o) check_exp(ttisclosure(o), gco2cl(val_(o).gc))
#define clLvalue(o) check_exp(ttisLclosure(o), gco2lcl(val_(o).gc))
#define clCvalue(o) check_exp(ttisCclosure(o), gco2ccl(val_(o).gc))
#define fvalue(o) check_exp(ttislcf(o), val_(o).f)
#define hvalue(o) check_exp(ttistable(o), gco2t(val_(o).gc))
#define bvalue(o) check_exp(ttisboolean(o), val_(o).b)
#define thvalue(o) check_exp(ttisthread(o), gco2th(val_(o).gc))
/* a dead value may get the 'gc' field, but cannot access its contents */
#define deadvalue(o) check_exp(ttisdeadkey(o), cast(void *, val_(o).gc))
#define l_isfalse(o) (ttisnil(o) || (ttisboolean(o) && bvalue(o) == 0))
#define iscollectable(o) (rttype(o) & BIT_ISCOLLECTABLE)
注意到,里面用到了幾個(gè)gco2x
的宏夕春,這組宏的作用是把GCObject的子類型轉(zhuǎn)為更具體的類型未荒,再次展開(kāi):
#define cast_u(o) cast(union GCUnion *, (o))
/* macros to convert a GCObject into a specific value */
#define gco2ts(o) \
check_exp(novariant((o)->tt) == LUA_TSTRING, &((cast_u(o))->ts))
#define gco2u(o) check_exp((o)->tt == LUA_TUSERDATA, &((cast_u(o))->u))
#define gco2lcl(o) check_exp((o)->tt == LUA_TLCL, &((cast_u(o))->cl.l))
#define gco2ccl(o) check_exp((o)->tt == LUA_TCCL, &((cast_u(o))->cl.c))
#define gco2cl(o) \
check_exp(novariant((o)->tt) == LUA_TFUNCTION, &((cast_u(o))->cl))
#define gco2t(o) check_exp((o)->tt == LUA_TTABLE, &((cast_u(o))->h))
#define gco2p(o) check_exp((o)->tt == LUA_TPROTO, &((cast_u(o))->p))
#define gco2th(o) check_exp((o)->tt == LUA_TTHREAD, &((cast_u(o))->th))
出現(xiàn)了一個(gè)新的聯(lián)合體:GCUnion
,這個(gè)是啥及志?展開(kāi)看:
/*
** Union of all collectable objects (only for conversions)
*/
union GCUnion {
GCObject gc; /* common header */
struct TString ts;
struct Udata u;
union Closure cl;
struct Table h;
struct Proto p;
struct lua_State th; /* thread */
};
哦片排,原來(lái)這個(gè)是上一篇的補(bǔ)丁。所有的GCObject都有共同的頭部困肩,也就是CommonHeader划纽,所以可以把GCObject轉(zhuǎn)為GCUnion之后,再轉(zhuǎn)為具體類型的指針锌畸,安全性由check_exp保證勇劣。
反之,又具體類型轉(zhuǎn)為GCObject也應(yīng)該提供:
/* macro to convert a Lua object into a GCObject */
#define obj2gco(v) \
check_exp(novariant((v)->tt) < LUA_TDEADKEY, (&(cast_u(v)->gc)))
set value
顯然潭枣,設(shè)置TValue的值比默,應(yīng)該同時(shí)設(shè)置Value和tag type。
/* Macros to set values */
#define settt_(o,t) ((o)->tt_=(t)) // 必須每次賦值都重新設(shè)置具體類型
#define setfltvalue(obj,x) \
{ TValue *io=(obj); val_(io).n=(x); settt_(io, LUA_TNUMFLT); }
#define setivalue(obj,x) \
{ TValue *io=(obj); val_(io).i=(x); settt_(io, LUA_TNUMINT); }
#define setnilvalue(obj) settt_(obj, LUA_TNIL)
#define setfvalue(obj,x) \
{ TValue *io=(obj); val_(io).f=(x); settt_(io, LUA_TLCF); }
#define setpvalue(obj,x) \
{ TValue *io=(obj); val_(io).p=(x); settt_(io, LUA_TLIGHTUSERDATA); }
#define setbvalue(obj,x) \
{ TValue *io=(obj); val_(io).b=(x); settt_(io, LUA_TBOOLEAN); }
#define setgcovalue(L,obj,x) \
{ TValue *io = (obj); GCObject *i_g=(x); \
val_(io).gc = i_g; settt_(io, ctb(i_g->tt)); }
#define setsvalue(L,obj,x) \
{ TValue *io = (obj); TString *x_ = (x); \
val_(io).gc = obj2gco(x_); settt_(io, ctb(x_->tt)); \
checkliveness(G(L),io); }
#define setuvalue(L,obj,x) \
{ TValue *io = (obj); Udata *x_ = (x); \
val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_TUSERDATA)); \
checkliveness(G(L),io); }
#define setthvalue(L,obj,x) \
{ TValue *io = (obj); lua_State *x_ = (x); \
val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_TTHREAD)); \
checkliveness(G(L),io); }
#define setclLvalue(L,obj,x) \
{ TValue *io = (obj); LClosure *x_ = (x); \
val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_TLCL)); \
checkliveness(G(L),io); }
#define setclCvalue(L,obj,x) \
{ TValue *io = (obj); CClosure *x_ = (x); \
val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_TCCL)); \
checkliveness(G(L),io); }
#define sethvalue(L,obj,x) \
{ TValue *io = (obj); Table *x_ = (x); \
val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_TTABLE)); \
checkliveness(G(L),io); }
#define setdeadvalue(obj) settt_(obj, LUA_TDEADKEY)
#define setobj(L,obj1,obj2) \
{ TValue *io1=(obj1); *io1 = *(obj2); \
(void)L; checkliveness(G(L),io1); }
這組宏在內(nèi)部都先重新引用下參數(shù)再操作盆犁,我想是為了避免宏展開(kāi)的副作用命咐,到處加小括號(hào),例如:
#define setsvalue(L,obj,x) \
{ TValue *io = (obj); TString *x_ = (x); \
val_(io).gc = obj2gco(x_); settt_(io, ctb(x_->tt)); \
checkliveness(G(L),io); }
這里把obj
和x
分別先賦值給io
和x_
谐岁,再使用醋奠,但下面的x_則因?yàn)橹挥昧艘淮尉蜎](méi)做這個(gè)賦值動(dòng)作:
#define setivalue(obj,x) \
{ TValue *io=(obj); val_(io).i=(x); settt_(io, LUA_TNUMINT); }
此外,這組宏里面伊佃,對(duì)GCObject窜司,都要通過(guò)obj2gco把具體類型轉(zhuǎn)型后再賦值給val_(io).gc字段。
待續(xù)
下一篇希望分析下Lua State對(duì)象航揉,也就是所謂的Thread总棵。