首先 lua是一门动态类型的脚本语言,这就意味着同一个变量在不同的时刻可以指向不同的数据类型,例如声明一个变量t 既可以指向字符串,也可以指向表,这就需要一个通用的数据结构来表示lua里的所有数据类型包括数字、字符串、表、指针、函数、Lua虚拟机等类型。用来表示不同的数据类型的通用数据结构需要满足两个条件,1.结构中需要包含一个表示数据类型的字段 2.包含一个存储实际数据的字段 ,常见的做法有两种:
1. 一般的面向对象思路,定义一个公共数据结构,存储所有数据类型的公共信息,其他具体数据在派生结构中定义 。
2. 使用c语言的union
在lua中,两种方法都有使用,接下来看一下lua的通用数据结构是如何定义的,首先需要确定数据类型type,在lua中基础数据类型对应的type在lua.h 中定义 。如下图
其中 LUA_TUSERDATA 和 LUA_TLIGHTUSERDATA 两种 对应的都是void* 。区别在于LUA_TLIGHTUSERDATA的分配释放由lua外部的使用者来完成,不需要lua关心其生命周期 ,后者是通过lua内部完成的。
上述数据类型大体可以分为两类,需要GC的和 不需要GC的,需要GC的有LUA_TSTRING(字符串)、LUA_TTABLE(表)’LUA_TFUNCTION(函数)、LUA_TUSERDATA(指针)LUA_TTHREAD(线程),对于需要GC的数据类型,他们的公共信息是一个简单的宏定义CommonHeader,定义在lobject.h中,并定义了一个只包含CommonHeader的结构体GCObject。
lua内部通过一个这个宏来标识需要GC的数据,此处使用宏定义是为了使用上的方便 。 next :下一个GC链表的成员 tt:表示上述的数据类型,lua_byte 是unsigned char 类型的别名 marked:GC相关标记位
接下来,Lua使用了一个联合体GCUnion来表示所有需要GC的数据类型(定义在lstate.h中)
再将需要所有需要GC的数据类型看作一种类型和 其他不需要GC的数据类型放在一起形成新的联合体Value (lobject.h)
需要注意的是:Value中存储的是一个GCObject的指针,实际指向的是一个GCUnion类型的值,这里的Value已经可以表示lua中所有数据类型的值。但还有一个问题,对于GC数据类型,在GCObject中有一个tt标识具体数据类型,而非GC数据类型却无法区分出来,所以还需要添加一个标识数据类型的字段 。也就是下面的Tvalue,其中的tt_标识了数据类型
这个结构就跟最开始我们自己定义的TValue十分类似了。到这里我们已经可以使用TValue来表示lua的所有数据类型,紧接着就需要实现各种具体类型和TValue之间进行转换 ,lobject.h中定义了大量的TValue和具体类型相互转换的宏定义
Tvalue -> 具体类型
首先进行类型判断,然后根据类型取值,对于非GC数据,直接取Vlaue中对应类型的值 ,对于GC数据需要先将Vlaue中的gc转为GCUnion* 再去取值 ,例如TString类型,通过(GCUnion*)(t.value_.gc)->ts 取值
具体类型 -> TValue
对于非gc数据,直接将值赋给对应字段,设置tt_为对应类型 ,对于gc数据类型,需要使用obj2gco将数据转换为GCObject,地址赋值给TVlaue中的gc字段,再设置tt_为对应类型 。lobject.h中主要是各种类型的数据结构定义 、类型判断函数 和 类型转换函数 。
需要知道的是 ,程序 = 数据结构 + 算法,而TValue就是为了实现lua动态语言特性所定义的一种数据结构。