lua数据类型

Lua是动态类型语言,变量不要类型定义,只需要为变量赋值。 值可以存储在变量中,作为参数传递或结果返回。

八种基本数据类型

Lua的类型系统是相对稳定的。很长时间里,Lua只有6个基本类型:nilnumberstringtablefunctionuserdata(实际上,直到Lua 3.0为止,之前的C函数和Lua函数有不同的内部类型,但是这个差异对于调用者是透明的)。唯一真正的改变来自于Lua 5.0,这个版本引入了两个新类型:threadboolean

1
2
3
4
5
6
7
8
9
10
11
#define LUA_TNONE	(-1)

#define LUA_TNIL 0
#define LUA_TBOOLEAN 1
#define LUA_TLIGHTUSERDATA 2 //void *
#define LUA_TNUMBER 3 //lua3后采用double精度表示数字整型
#define LUA_TSTRING 4 //TString
#define LUA_TTABLE 5 //Table
#define LUA_TFUNCTION 6 //CClosure、LClosure
#define LUA_TUSERDATA 7 //void *
#define LUA_TTHREAD 8 //协程

变量第一次赋值之前,值为nil

第一类值

lua中所有值都是第一类值,他们可以存在变量中(无论全局变量还是局部变量)或table中,可以作为函数参数,可以作为返回值。

Lua通用数据结构的实现

C语言中实现通用数据结构的一般做法

如果要使用一个通用的数据结构来表示不同的数据类型,一般的做法是:

  • 需要一个字段来存储数据的类型
  • 需要存储不同的数据类型的数据

这里有这两种实现方法

  1. 定义一个公共的数据结构作为基础类型,里面存储的都是表达这个数据的基础信息,其他具体的类型是从这里派生出来的 。 这就是一般的面向对象的思路。
  2. 使用联舍( union )来将所有数据包进来

上面两种做法各有利弊 。 在Lua代码中,一般采用两种做法相结合的方式。

具体实现

Lua需要进行GC操作的数据类型都会有一个CommonHeader宏定义的成员,并且这个成员在结构体定义的最开始部分。

1
#define CommoHeader GCObject *next; lu_byte tt; lu_byte marked

任何需要进行垃圾回收处理的Lua数据类型,必然以CommonHeader作为该结构体定义的最开始部分。如果熟悉C++类的实现原理,可以将CommoHeader这个成员理解为一个基类的所有成员,而其他需要回收处理的数据类型均从这个基类继承下来,所以它们的结构体定义的开始部分就是这个成员。

还有一个名为GCheader的结构体,其中的成员只有Common Header

1
2
3
typedef struct GCheader {
CommonHeader;
} GCheader;

于是,在Lua中就使用了GCObject联合体将所有需要进行垃圾回收的数据类型囊括了进来:

1
2
3
4
5
6
7
8
9
10
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中将GCObject和它们一起放在了联合体Value中:

1
2
3
4
5
6
typedef union {
GCObject *gc;
void *p;//LUA_TLIGHTUSERDATA
lua_Number n;//LUA_TNUMBER
int b;//LUA_TBOOLEAN
} Value;

到了这一步 ,差不多可以表示Lua中所有的数据类型了。但是还欠缺一点东西,那就是这些数据到底是什么类型的。于是Lua代码中又有了TValue,它用于将Value和类型结合在一起。

1
2
3
4
5
#define TValuefields	Value value; int tt

typedef struct lua_TValue {
TValuefields;
} TValue;

前面提到过, Lua同时采用了两种方式来做到数据统一。根据前面的分析,这表现在以下两个方面。

  • 具体类型中有CommonHeader用来存放所有数据类型都通用的字段
  • TValue作为统一表示所有数据的数据结构,内部使用了联合体Value将所有数据都包起来

在具体的代码中,TValue用于统一地表示数据,而一旦知道了具体的类型,就需要使用具体的类型了。因此,代码中有不少涉及TValue与具体类型之间转换的代码,其主要逻辑都是将TValue中的ttvalue与具体类型的数据进行转换。
如将lua Number转换为TValue的宏setnvalue

1
2
#define setnvalue(obj,x) \
{ TValue *i_o=(obj); i_o->value.n=(x); i_o->tt=LUA_TNUMBER; }

从lua-5.1.1中分离出来的动态类型实现代码

ltypes

reference

《Lua设计与实现》


Debemos esforzarnos por vencer todas las dificultades y procurar el mejor resultado posibl.