Lua数据类型(源码解析)

我们都知道Lua是一门动态类型的脚本语言,也就是说同一个变量可以在不同的时刻指向不同类型的数据。例如

local a = nil
a = 1
a = "123"

而在Lua中有8中基础的数据类型:nil(空),boolean(布尔),number(数字),string(字符串),table(表),function(函数),userdata(自定义类型),thread(协程),那这几种基础类型在Lua中是怎么定义的,而Lua又是怎么实现动态类型的呢?

一、C语言中实现通用数据结构的设想

Lua是使用C语言实现的一种脚本语言,那么我们如果我们想在C语言中实现一种新的通用的数据类型一般会怎么去做呢?
定义一个结构体,这个结构体中一定要有一个字段来存储当前结构体所表示的数据类型,同时需要一些字段来存储不同数据类型的具体数据

二、Lua中通用数据结构的实现
2.1基本数据类型宏的定义

先看一下Lua中定义的几种基本数据类型的宏:

//(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

这些宏对应的数据类型如下表:


定义的宏对应的数据类型.png

lua_Number对应的C语言的基本数据类型double,所以Lua中的number类型表示的都是实数(双精度浮点数),Lua中没有整数类型。

//(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 *(指针类型)。根据名字我们知道他们对应的是Lua的userdata基本数据类型,但是两者是有一些区别的。LUA_TLIGHTUSERDATA表示那些内存分配与释放都是由Lua外部的使用者要管理的对象,而LUA_TUSERDATA表示的都是通过Lua来管理生命周期的对象,也就是LUA_TUSERDATA指向的对象是需要加入到Lua的GC(Garbage Collection 垃圾回收)中的。

2.2需要GC的基本数据类型

我们知道Lua有自己的GC机制,那么哪些基础数据类型需要加到GC中,哪些又不需要,怎么区分呢?
在Lua中用一个宏来表示哪些数据类型需要进行GC操作:

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

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

所以说LUA_TSTRING之前的数据类型是都不需要GC,也就是string,table,function,userdata,thread都需要GC的。
在Lua中需要进行GC操作的数据类型都会有个CommonHeader宏定义的成员,并且这个成员在定义的最开始部分。

/*
** 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 : 指向下一个GC链表的成员
tt : 表示数据的类型,即前面的那些表示数据类型的宏
marked :GC时,相关的标记位。
到了这里我们可以使用一个共同体(union),将所有需要进行GC的数据类型囊括起来:

//(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的数据类型。

2.3Lua中所有数据类型表示

既然所有需要GC的数据类型使用GCObject表示,那么同理所有的数据类型也可以用一个共同体表示:

//(lobject.h)
/*
** Union of all Lua values
*/
typedef union {
  GCObject *gc;
  void *p;
  lua_Number n;
  int b;
} Value;

结合我们(一)中所说的,我们现在有了可以表示所有类型数据的Value了,那么就还需要一个表示数据类型的字段,所以Lua中给我们定义了一个TValue的结构体:

//(lobject.h)
/*
** Tagged Values
*/

#define TValuefields    Value value; int tt

typedef struct lua_TValue {
  TValuefields;
} TValue;

从这个结构体我们可以看到使用int类型的tt字段表示当前的数据类型,使用Value来表示任意类型的值。这样TValue就可以表示Lua中任意的数据类型了。
我们用一个图来表示一下Lua通用的数据结构的组织:


Lua通用数据类型组织结构.png
三、通用类型与具体类型转换
3.1判断是否是具体的数据类型
//(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为通用数据类型TValue,tt则为TValue结构体中表示具体数据类型的字段。

3.2获得具体数据类型的值
//(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设置具体数据类型的值
//(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); }
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,783评论 5 472
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,396评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,834评论 0 333
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,036评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,035评论 5 362
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,242评论 1 278
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,727评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,376评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,508评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,415评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,463评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,140评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,734评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,809评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,028评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,521评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,119评论 2 341

推荐阅读更多精彩内容

  • Lua内部采用一种通用的基础数据结构来表示所有数据类型,Lua语言及其精简,只有字符串和表两种最基本的数据结构。然...
    JunChow520阅读 2,280评论 0 1
  • 1. 写在前面 很多时候我们都需要借助一些脚本语言来为我们实现一些动态的配置,那么就会涉及到如何让脚本语言跟原生语...
    杰嗒嗒的阿杰阅读 3,421评论 9 31
  • lua 的值类型 lua 是动态类型的语言,即是说类型附着于值而不是变量。在 lua 脚本里,变量是没有类型的,只...
    董哒哒阅读 2,573评论 1 1
  • 开篇 上一节分析了 lua_arith 的大部分代码,由于篇幅原因,留到本节将继续讲解剩余的部分: 解析 现在我们...
    码上说阅读 778评论 0 1
  • 第一篇 语言 第0章 序言 Lua仅让你用少量的代码解决关键问题。 Lua所提供的机制是C不擅长的:高级语言,动态...
    testfor阅读 2,638评论 1 7