userdata说明:
0、Lua中使用userdata类型来表示在C中定义的类型。userdata只是提供了一块原始的内存区域,可以用来存储任何东西,并且,在lua中userdata没有任何预定义的操作。在C中调用函数lua_newuserdata会根据指定的大小分配一块内存,并将相应的userdata压入栈中,最后返回这个内存块的地址:void *lua_newuserdata(lua_State*L,size_t size)。
1、实质在C中定义lua的userdata,与定义C模块完全类似,只不过通常这时需要通过调用lua_newuserdata来告诉lua分配一块额外的内存,而在内存所有相关的操作都是在C中的定义的,实质就是C模块中的接口。注意这块分配的额外内存是由Lua垃圾收集器来管理的,无须关心起释放等情况。
2、在实现一个Lua的程序库或userdate,必须保证该库或userdata的接口不应破坏C数据或在Lua中导致core dump。
3、可以为每种userdata创建一个唯一的元表,来辨别不同类型的userdata,每当创建了一个userdata后,就用相应的元表来标记它,而每得到一个userdata后,就检查它是否拥有正确的元表,注意Lua代码中不能改变userdata的元表(当能增加已有元表的属性,比如对元表key为__index赋值)。通常是将这个元表存储在注册表中,也类型名作为key,元表为value。辅助库提供了一些函数来实现这些:
int luaL_newmetatable(lua_State*L, const char *tname);
void luaL_getmetatable(lua_State *L,const char *tnaem);
void *luaL_checkudata(lua_State*L,int index,const char *tname);
4、轻量级userdata是一种表示C指针的值(即void*),要将一个轻量级userdata放入栈中,只需要调用lua_pushlightuserdata即可。轻量级userdata只是一个指针而已。它没有元表,就像数字一样,轻量级userdata无须受垃圾收集器的管理。
5、Lua在释放完全userdata所关联的内存时,若发现userdata对应的元表还有__gc元方法,则会调用这个方法,并以userdata自身作为参数传入。利用该特性,可以再回收userdata的同时,释放与此userdata相关联的资源。
full userdata
static struct StudentTag
{
char *strName; // 学生姓名
char *strNum; // 学号
int iSex; // 学生性别
int iAge; // 学生年龄
};
//lua 中通过调用这个接口来得到 pStudent指针
static int Student(lua_State *L)
{
size_t iBytes = sizeof(struct StudentTag);
struct StudentTag *pStudent;
pStudent = (struct StudentTag *)lua_newuserdata(L, iBytes);
return 1; // 新的userdata已经在栈上了
}
static int GetName(lua_State *L)
{
struct StudentTag *pStudent = (struct StudentTag *)lua_touserdata(L, 1);
luaL_argcheck(L, pStudent != NULL, 1, "Wrong Parameter");
lua_pushstring(L, pStudent->strName);
return 1;
}
static int SetName(lua_State *L)
{
// 第一个参数是userdata
struct StudentTag *pStudent = (struct StudentTag *)lua_touserdata(L, 1);
luaL_argcheck(L, pStudent != NULL, 1, "Wrong Parameter");
// 第二个参数是一个字符串
const char *pName = luaL_checkstring(L, 2);//检查第2个参数是不是string,并返回参数值
luaL_argcheck(L, pName != NULL && pName != "", 2, "Wrong Parameter");
pStudent->strName = (char *)pName;
return 0;
}
static int GetAge(lua_State *L)
{
struct StudentTag *pStudent = (struct StudentTag *)lua_touserdata(L, 1);
luaL_argcheck(L, pStudent != NULL, 1, "Wrong Parameter");
lua_pushinteger(L, pStudent->iAge);
return 1;
}
static int SetAge(lua_State *L)
{
struct StudentTag *pStudent = (struct StudentTag *)lua_touserdata(L, 1);
luaL_argcheck(L, pStudent != NULL, 1, "Wrong Parameter");
int iAge = luaL_checkinteger(L, 2);
luaL_argcheck(L, iAge >= 6 && iAge <= 100, 2, "Wrong Parameter");
pStudent->iAge = iAge;
return 0;
}
static int GetSex(lua_State *L)
{
// 这里由你来补充
struct StudentTag *pStudent = (struct StudentTag *)lua_touserdata(L, 1);
luaL_argcheck(L, pStudent != NULL, 1, "get wrong arg from lua");
lua_pushnumber(L, pStudent->iSex);//通过C++操作把数据放入到堆栈中, 1表示男 2表示女
return 1;
}
static int SetSex(lua_State *L)
{
// 这里由你来补充
struct StudentTag *pStudent = (struct StudentTag *)lua_touserdata(L, 1);
luaL_argcheck(L, pStudent != NULL, 1, "get wrong arg from lua");
int iSex = luaL_checkinteger(L, 2);
luaL_argcheck(L, iSex == 1 || iSex == 2, 2, "get wrong arg from lua");
pStudent->iSex = iSex;
return 0;
}
static int GetNum(lua_State *L)
{
// 这里由你来补充
return 1;
}
static int SetNum(lua_State *L)
{
// 这里由你来补充
return 0;
}
static struct luaL_reg arrayFunc[] =
{
{"new", Student},//注册成为Student的接口
{"getName", GetName},
{"setName", SetName},
{"getAge", GetAge},
{"setAge", SetAge},
{"getSex", GetSex},
{"setSex", SetSex},
{"getNum", GetNum},
{"setNum", SetNum},
{NULL, NULL}
};
int luaopen_userdatademo1(lua_State *L)
{
luaL_register(L, "Student", arrayFunc);
luaL_dofile(L, "main.lua");
return 1;
}
.lua文件代码:
local objStudent = Student.new()
Student.setName(objStudent, "果冻想")
Student.setAge(objStudent, 15)
Student.setSex(objStudent, 2)
local strName = Student.getName(objStudent)
local iAge = Student.getAge(objStudent)
local iSex = Student.getSex(objStudent)
print("1")
print(strName)
print(iAge)
print("2")
print("iSex" .. iSex)
轻量级userdata
轻量级userdata是一种表示C指针的值(即void *)。由于它是一个值,所以不用创建它。要将一个轻量级userdata放入栈中,只需要调用lua_pushlightuserdata即可。
void lua_pushlightuserdata(lua_State *L,void *p);
尽管两种userdata在名称上差不多,但它们之间还是存在很大不同的。轻量级userdata不是缓冲,只是一个指针而已。它也没有元表,就像数字一样,轻量级userdata不受到垃圾收集器的管理。
轻量级userdata的真正用途是相等性判断。一个完全userdata是一个对象,它只与自身相等。而一个轻量级userdata则表示了一个C指针的值。因此,它与所有表示同一个指针的轻量级userdata相等。可以将轻量级userdata用于查找Lua中的C对象。
// 压入轻量级userdata,一个static变量的地址
static char key = 'k';
lua_pushlightuserdata(L, (void *)&key);
lua_pushstring(L, "JellyThink");
lua_settable(L, LUA_REGISTRYINDEX);
由于静态变量的地址在一个进程中具有唯一性,所以绝对不会出现重复key的问题。
// 从注册表中取对应的值
lua_pushlightuserdata(L, (void *)&key);
lua_gettable(L, LUA_REGISTRYINDEX);
C++中代码:
lua_State *L = luaL_newstate();
if (L)
{
luaL_openlibs(L);
}
lua_pushnumber(L, 20);
lua_setglobal(L, "dde");
static char key = 'k';
lua_pushlightuserdata(L, (void *)&key);
lua_setglobal(L, "ddee");
luaL_dofile(L, "tabletest.lua");
.lua代码
print("sdfe" .. dde)
print("type" .. type(ddee))
结果:
————————————————
版权声明:本文为优快云博主「从小就爱吃肉」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.youkuaiyun.com/dugaoda/article/details/50259497