Lua數(shù)據(jù)類型(源碼解析)

我們都知道Lua是一門動(dòng)態(tài)類型的腳本語言,也就是說同一個(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語言中實(shí)現(xiàn)通用數(shù)據(jù)結(jié)構(gòu)的設(shè)想

Lua是使用C語言實(shí)現(xiàn)的一種腳本語言,那么我們?nèi)绻覀兿朐贑語言中實(shí)現(xiàn)一種新的通用的數(shù)據(jù)類型一般會(huì)怎么去做呢?
定義一個(gè)結(jié)構(gòu)體,這個(gè)結(jié)構(gòu)體中一定要有一個(gè)字段來存儲(chǔ)當(dāng)前結(jié)構(gòu)體所表示的數(shù)據(jù)類型,同時(shí)需要一些字段來存儲(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

這些宏對應(yīng)的數(shù)據(jù)類型如下表:


定義的宏對應(yīng)的數(shù)據(jù)類型.png

lua_Number對應(yīng)的C語言的基本數(shù)據(jù)類型double,所以Lua中的number類型表示的都是實(shí)數(shù)(雙精度浮點(diǎn)數(shù)),Lua中沒有整數(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ù)名字我們知道他們對應(yīng)的是Lua的userdata基本數(shù)據(jù)類型,但是兩者是有一些區(qū)別的。LUA_TLIGHTUSERDATA表示那些內(nèi)存分配與釋放都是由Lua外部的使用者要管理的對象,而LUA_TUSERDATA表示的都是通過Lua來管理生命周期的對象,也就是LUA_TUSERDATA指向的對象是需要加入到Lua的GC(Garbage Collection 垃圾回收)中的。

2.2需要GC的基本數(shù)據(jù)類型

我們知道Lua有自己的GC機(jī)制,那么哪些基礎(chǔ)數(shù)據(jù)類型需要加到GC中,哪些又不需要,怎么區(qū)分呢?
在Lua中用一個(gè)宏來表示哪些數(shù)據(jù)類型需要進(jìn)行GC操作:

//(lobject.h)
#define ttype(o)    ((o)->tt)

#define iscollectable(o)    (ttype(o) >= LUA_TSTRING)

所以說LUA_TSTRING之前的數(shù)據(jù)類型是都不需要GC,也就是string,table,function,userdata,thread都需要GC的。
在Lua中需要進(jìn)行GC操作的數(shù)據(jù)類型都會(huì)有個(gè)CommonHeader宏定義的成員,并且這個(gè)成員在定義的最開始部分。

/*
** 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ù)類型囊括起來:

//(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é)合我們(一)中所說的,我們現(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來表示任意類型的值。這樣TValue就可以表示Lua中任意的數(shù)據(jù)類型了。
我們用一個(gè)圖來表示一下Lua通用的數(shù)據(jù)結(jié)構(gòu)的組織:


Lua通用數(shù)據(jù)類型組織結(jié)構(gòu).png
三、通用類型與具體類型轉(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則為TValue結(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); }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Lua內(nèi)部采用一種通用的基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)來表示所有數(shù)據(jù)類型,Lua語言及其精簡,只有字符串和表兩種最基本的數(shù)據(jù)結(jié)構(gòu)。然...
    JunChow520閱讀 2,564評論 0 1
  • 1. 寫在前面 很多時(shí)候我們都需要借助一些腳本語言來為我們實(shí)現(xiàn)一些動(dòng)態(tài)的配置,那么就會(huì)涉及到如何讓腳本語言跟原生語...
    杰嗒嗒的阿杰閱讀 3,500評論 9 31
  • lua 的值類型 lua 是動(dòng)態(tài)類型的語言,即是說類型附著于值而不是變量。在 lua 腳本里,變量是沒有類型的,只...
    董噠噠閱讀 2,804評論 1 1
  • 開篇 上一節(jié)分析了 lua_arith 的大部分代碼,由于篇幅原因,留到本節(jié)將繼續(xù)講解剩余的部分: 解析 現(xiàn)在我們...
    碼上說閱讀 894評論 0 1
  • 第一篇 語言 第0章 序言 Lua僅讓你用少量的代碼解決關(guān)鍵問題。 Lua所提供的機(jī)制是C不擅長的:高級語言,動(dòng)態(tài)...
    testfor閱讀 2,936評論 1 7

友情鏈接更多精彩內(nèi)容