1、概述
Lua中userdata分两种,一种是轻量级userdata(light userdata),轻量级userdata是一种表示C指针的值,对Lua虚拟机来说,这种数据类型不需要GC(垃圾回收),其指向的内存由用户分配和释放,其实现就是一个void *p指针;后一种userdata类型完全userdata(full userdata),内存是由Lua虚拟机分配,并有GC机制负责处理。下面将通过Lua 5.2.1的源码来看后一种userdata的实现。
2、源码实现
成员dummy和CommonHeader含义与TString完全一样。其他成员含义如下:
struct Table *metatable:指向userdata对应的元表,也就是一个table。可以调用luaL_newmetatable创建一个元表(注意是在注册表上创建的,也就是说userdata的元表保存在注册表中),然后调用lua_setmetatable来设置userdata的元表,注意Lua代码中不能改变userdata的元表。
struct Table *env:userdata的环境,创建userdata时该值为NULL。可以通过调用lua_setuservalue函数来设置userdata的环境。
size_t len:保存userdata内存的大小,就紧随头后面数据内存的大小。
userdata的数据部分和字符串一样,都是紧接着放在结构后面,创建userdata的代码如下(lstring.c):
II、 userdata的元表通常用来存储对应C/C++类型的方法,这样在脚本中可以直接调用userdata对应的方法,并且这些方法也是用C/C++实现的。
III、 创建一个新的userdata时,其环境表默认值是空,即成员值env 为NULL,当前这个成员在Lua没被使用。在一些文章指出,这个成员可以保存与userdata实例相关的数据,而其对应的元表保存userdata的方法。
Lua中userdata分两种,一种是轻量级userdata(light userdata),轻量级userdata是一种表示C指针的值,对Lua虚拟机来说,这种数据类型不需要GC(垃圾回收),其指向的内存由用户分配和释放,其实现就是一个void *p指针;后一种userdata类型完全userdata(full userdata),内存是由Lua虚拟机分配,并有GC机制负责处理。下面将通过Lua 5.2.1的源码来看后一种userdata的实现。
2、源码实现
userdata内存存储形式类似于字符串,以一个头开始然后紧随头后面保存相关的数据,但是每个userdata都拥有自己独立的元表。下面是userdata对应的头数据结构Udata代码(lobject.h):
成员dummy和CommonHeader含义与TString完全一样。其他成员含义如下:
struct Table *metatable:指向userdata对应的元表,也就是一个table。可以调用luaL_newmetatable创建一个元表(注意是在注册表上创建的,也就是说userdata的元表保存在注册表中),然后调用lua_setmetatable来设置userdata的元表,注意Lua代码中不能改变userdata的元表。
struct Table *env:userdata的环境,创建userdata时该值为NULL。可以通过调用lua_setuservalue函数来设置userdata的环境。
size_t len:保存userdata内存的大小,就紧随头后面数据内存的大小。
userdata的数据部分和字符串一样,都是紧接着放在结构后面,创建userdata的代码如下(lstring.c):
lua给 c/c++暴露的接口
LUA_API void *lua_newuserdata (lua_State *L, size_t size) { Udata *u; lua_lock(L); u = luaS_newudata(L, size); //在lua中创建出一个userdata数据结构 setuvalue(L, L->top, u); //然后将其放置在栈顶 api_incr_top(L); //增加栈索引 luaC_checkGC(L); lua_unlock(L); return getudatamem(u); //返回存储数据的内存地址 }可以看到这里主要是调用了luaS_newudata 最后返回分配的内存地址
#define getudatamem(u) \
check_exp(sizeof((u)->len), (cast(char*, (u)) + sizeof(UUdata)))
check_exp 检测分配的字节数是否为0
3、总结
I、
II、 userdata的元表通常用来存储对应C/C++类型的方法,这样在脚本中可以直接调用userdata对应的方法,并且这些方法也是用C/C++实现的。
III、 创建一个新的userdata时,其环境表默认值是空,即成员值env 为NULL,当前这个成员在Lua没被使用。在一些文章指出,这个成员可以保存与userdata实例相关的数据,而其对应的元表保存userdata的方法。