lua类型 | lua示例 | C类型(宏 ) | C子类型(variant tags宏) 及详细说明 | C数据结构 |
nil(空) | type(nil) -->nil | #define LUA_TNIL 0 //空类型 | 无 | |
boolean(布尔) | type(true) -->boolean | #define LUA_TBOOLEAN 1 | 不被GC管理 | int |
number(数值) |
type(3.14) -->number type(100) -->number // 若想知道number具体类型,可使用函数math.type来获取 math.type(3.14) -->float math.type(100) -->integer 注:对于其他非number类型,math.type会返回nil |
#define LUA_TNUMBER 3 | #define LUA_TNUMFLT (LUA_TNUMBER | (0 << 4)) /* float numbers */ 3 不被GC管理 |
lua_Number //即double |
#define LUA_TNUMINT (LUA_TNUMBER | (1 << 4)) /* integer numbers */ 19 不被GC管理 |
lua_Integer //即__int64 |
|||
string(字符串) |
type("Hello World") -->string type('good') -->string |
#define LUA_TSTRING 4 |
#define LUA_TSHRSTR (LUA_TSTRING | (0 << 4)) /* short strings */ 4 被GC管理 |
TString |
#define LUA_TLNGSTR (LUA_TSTRING | (1 << 4)) /* long strings */ 20 被GC管理 |
TString | |||
function(函数) |
type(print) -->function |
#define LUA_TFUNCTION 6 |
#define LUA_TLCL (LUA_TFUNCTION | (0 << 4)) /* Lua closure */ 6 被GC管理 注:Lua closure即Lua function LClosure 由 Proto 和 UpVal 组成 Proto描述了lua函数的函数原型 记录着函数原型的字节码、函数引用的常量表、调试信息、参数、栈大小等信息 UpVal保存了对upvalue的引用。它直接用一个TValue 指针引用一个upvalue值变量 当被引用的变量还在数据栈上时,这个指针直接指向栈上的TValue,那么这个upvalue被称为开放的 |
LClosure |
#define LUA_TLCF (LUA_TFUNCTION | (1 << 4)) /* light C function */ 22 不被GC管理 LUA_TLCF即:当LUA_TCCL不包含 upvalue时,直接用lua_CFunction函数指针,不必构造Closure对象 注:typedef int (*lua_CFunction) (lua_State *L) |
lua_CFunction | |||
#define LUA_TCCL (LUA_TFUNCTION | (2 << 4)) /* C closure */ 38 被GC管理 注:C closure即regular C function CClosure 由 lua_CFunction 和 TValue 组成 C 函数可以用闭包的方式嵌入 lua,与LClosure 相比,CClosure天生就是关闭的 因此,直接使用TValue来保存upvalue |
CClosure |
|||
table(表) | type({}) -->table | #define LUA_TTABLE 5 | Table | |
userdata(用户数据) |
type(io.stdin) -->userdata 注:stdin,stdout,stderr是lua提供三种预定义文件描述 |
#define LUA_TLIGHTUSERDATA 2 |
即轻量级用户数据(light userdata) 只是一个指针(在c中,调用lua_pushlightuserdata将一个指针压入栈来给lua使用) 没有元表无法得知其类型 与数值类型一样,不被GC管理 |
void* |
#define LUA_TUSERDATA 7 |
即完全用户数据(full userdata) 通常用来表示C中的结构体,可以有元表和元方法 在c中调用lua_newuserdata创建指定大小的内存区域,被GC管理 |
void* | ||
thread(线程) |
type(coroutine.create(function() end)) -->thread |
#define LUA_TTHREAD 8 |
lua不支持真正的多线程,实际是一个协程 在c中调用lua_newstate来创建lua_State 在c中调用lua_newthread创建一个线程 被GC管理 |
lua_State |
#define LUA_TNONE (-1) //无类型 |
无 |
基础结构
Value与TValue
lua为了方便对所有的类型进行统一管理,把它们都抽象成了一个叫做Value的union结构中
/* ** Union of all Lua values */ typedef union Value { GCObject *gc; /* collectable objects */ void *p; /* light userdata */ int b; /* booleans */ lua_CFunction f; /* light C functions */ lua_Integer i; /* integer numbers */ lua_Number n; /* float numbers */ } Value;
从定义可以看出,主要把这些类型划分为了需要GC的类型和不需要GC的类型
由于Value是union的结构,所以每个Value实例里同时只会有一个字段是有效的
而为了知道具体哪个字段是有效的,也就是具体该Value是什么类型,从而有了TValue这个struct结构,主要在Value基础上wrap了一个_tt字段来标识Value的具体类型
#define TValuefields Value value_; int tt_ typedef struct lua_TValue { TValuefields; } TValue;
GCUnion、GCObject、CommonHeader
lua把所有值按是否需要被GC,划分为了一般类型和被GC管理的类型。所有需要被GC的类型,被定义在了GCUnion里
/* ** Union of all collectable objects (only for conversions) */ union GCUnion { GCObject gc; /* common header */ struct TString ts; /* 字符串 */ struct Udata u; /* 用户数据 */ union Closure cl; /* 函数 */ struct Table h; /* 表 */ struct Proto p; /* 函数原型:存放函数字节码等信息 */ struct lua_State th; /* 线程 */ };
可以发现String、UserData、Closure、Table、Proto、luaState等类型都是需要被GC的,GCUnion结构和Value类似,也是同时只有一个字段是有效的
所以我们自然而然会想到,是不是类似TValue一样,在外面给包一层type呢,但是lua实现这边并没有这样做
而是让TString、UData这些"子类"都在各自开头定义了一个叫做CommonHeader的宏,这个宏里包含了type和一些其他字
而每个GC类型都需要在在其struct头部定义该宏,从而可以造成一种所有GC类型都继承自一个带有CommonHeader宏的基类的假象
/* ** Common type for all collectable objects */ typedef struct GCObject GCObject; /* ** 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 // 注:tt,即该GC对象的具体类型 // 注:next,指向GCObject的指针,用于GC算法内部实现链表 // 注:marked,用于GC算法内部实现 /* ** Common type has only the common header */ struct GCObject { CommonHeader; };
这样组织的好处在于lua可以把所有的GC类型的对象都视作是一个GCObject。
再比如,lua里创建单个gcobject的函数如下
/* ** create a new collectable object (with given type and size) and link ** it to 'allgc' list. */ GCObject *luaC_newobj (lua_State *L, int tt, size_t sz) { global_State *g = G(L); GCObject *o = cast(GCObject *, luaM_newobject(L, novariant(tt), sz)); o->marked = luaC_white(g); o->tt = tt; o->next = g->allgc; g->allgc = o; return o; }
所有的gc类型就都会调用luaC_newobj函数来创建一个GCObject实例,区别只是在于传入的type和内存size不一样而已
该函数会根据实际传入的内存大小来开辟空间,然后填充CommonHeader部分的数据
最后,它还会把该obj挂接到global_state结构里定义的GC列表GCObject* allgc(保存所有gc类型对象的指针)的头部,以供GC模块使用
每个类型只用把创建出来的实例剩余内存部分的数据设置好即可,比如下面的String类型
#define sizelstring(l) (sizeof(union UTString) + ((l) + 1) * sizeof(char)) /* ** Get the actual string (array of bytes) from a 'TString'. ** (Access to 'extra' ensures that value is really a 'TString'.) */ #define getstr(ts) check_exp(sizeof((ts)->extra), cast(char *, (ts)) + sizeof(UTString)) #define gco2ts(o) check_exp(novariant((o)->tt) == LUA_TSTRING, &((cast_u(o))->ts)) /* ** creates a new string object */ static TString *createstrobj (lua_State *L, size_t l, int tag, unsigned int h) { TString *ts; GCObject *o; size_t totalsize; /* total size of TString object */ // 计算一个string实例实际内存占用大小:其实是UTString结构占用,再加上(charlength+1)个char大小 totalsize = sizelstring(l); // 创建GCObject o = luaC_newobj(L, tag, totalsize); ts = gco2ts(o); // 填充string实例特有字段 ts->hash = h; ts->extra = 0; // 取TString关联的char数组 getstr(ts)[l] = '