TValue結構
TValue這個結構體是Lua的通用結構體,,Lua中的所有的數據都可以使用這個結構體來表示.很容易想到,在面向對象中,這個結構體是一個基類,派生出來的都是其他的子類.
TValue結構體內部有幾個宏, 展開之后就是這樣的:
typedef struct lua_TValue {
union {
union GCObject {
struct GCheader {
GCObject *next; lu_byte tt; lu_byte marked;
} gch;
union TString ts;
union Udata u;
union Closure cl;
struct Table h;
struct Proto p;
struct UpVal uv;
struct lua_State th; /* thread */
} gc;
void *p;
lua_Number n;
int b;
} value;
int tt;
} TValue;
這個結構體定義,總體來說分為兩個部分:tt存放的數據類型,而value域存放的是各種數據.而在其中,又划分為兩個部分,可gc的數據類型使用union放在一起,剩下的就是不可gc的數據類型了:void*,lua_Number,int.
gc union
gc union的定義,可以看到各種可gc的類型(Tstring,Udata..etc)和一個GCHeader放在 一起,也就是說,當這部分還是數據的時候,數據部分啟用,否則就是gc部分了.這里的GCHeader包括了三個部分:next指針將可gc的數據串聯成鏈表,tt表示數據類型,marked存放的gc處理時的顏色值.
這是另一種方式的使用C語言實現的面向對象,對外部而言,TValue結構體可以看作是”基類”,真正進行處理時,再根據數據類型決定到底使用value union中的哪個數據部分.可以看到lua源代碼中定義了很多宏就是這樣操作Tvalue數據指針的,比如:
#define hvalue(o) check_exp(ttistable(o), &(o)->value.gc->h)
這個宏定義了如何從TValue指針得到Table結構體:首先判斷數據類型是Table,然后將value的gc union中Table *h取出.
反之,要從一個具體的類型轉換再賦值為相應的TValue,Lua源代碼中也提供了相應的宏.因為TValue結構體的中的value域是一個union,所以其實隨便強制轉換為其中的哪一種類型都可以,不過看上去最舒服的寫法還是直接轉換為公共類型GCObject了,比如:
#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); }
GCObject
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 */
};
其中的GCheader展開是這樣的:
typedef struct GCheader {
CommonHeader;
} GCheader;
而隨便抽在GCObject結構體中的數據類型結構體定義,都發現也包含了一個CommonHeader結構體,比如:
typedef struct Table {
CommonHeader;
lu_byte flags;
lu_byte lsizenode; /* log2 of size of `node' array */
struct Table *metatable;
TValue *array; /* array part */
Node *node;
Node *lastfree; /* any free position is before this position */
GCObject *gclist;
int sizearray; /* size of `array' array */
} Table;
換言之,在GCObject中,無論是哪個數據結構體,都自己有一份CommonHeader.仔細觀察,其實GCObject這個union的內存分布,最開始部分無論如何都是留給CommonHeader的.這樣做,就保證了一個存放在TValue結構體中的數據,既可以使用CommonHeader關於GC的部分,也可以使用到自己本身的數據部分了.