C语言中代码调用如下:
static int InitName(lua_State *L){ // 第 1 步 struct People *pPeople = (struct People *)lua_touserdata(L, 1); // 第 2 步 if(pPeople != NULL) { pPeople->name = "MaNong"; } return 1;}
但是在实际上,调用远远没有这么简单。
上面代码第1步从Lua栈顶获取一个UserData对象,如果不是一个UserData对象,这里会返回空指针,第2步判断失败,没有问题。
但如果Lua栈顶确实是一个UserData对象,但并不是对应People结构体的UserData对象,那这里就很大问题了。这里做的操作就相当于取出一块内存,并强制当作是People结构对象,然后去操作内存里面的某个地址,尝试修改为"MaNong"字符串。这里强制修改内存,将可能对程序造成不可预估的错误。
所以,我们在C语言中取UserData的时候要判断这个UserData是否是我们需要的!
这个判断是通过__name元方法实现的,这个元方法可以记录一个字符串,不同的UserData类型使用不同的字符串,则通过这个名字判断我们就可以知道取出来的UserData是否是我们需要的那个UserData了。
要使用元方法,就需要用到元表。Lua源码中已经为我们提供了相应的方法,见源码《lauxlib.c》中luaL_newmetatable:
这个函数的功能就是在全局环境中创建一个元表,然后设置该元表的__name为参数中的tname字符串,再配合下面这个函数使用的:
见源码《lauxlib.c》中luaL_setmetatable,作用是设置栈顶元素的metatable为叫指定__name的metatable:
于是,我们此时补全一下例子中的代码:
// 只需要提前调用一次,把__name为"People"的元表注册到Luastatic int RegisterPeopleMetatable(lua_State *L){ luaL_newmetatable(L, "People"); return 1;} static int People(lua_State *L){ struct People *pPeople = (struct People *)lua_newuserdata(L, sizeof(struct People)); // 设置上面这个UserData的metatable为已经注册好的__name为"People"的元表 luaL_setmetatable(L, "People"); return 1; // 新的userdata已经在栈上了}
此时,创建出来的People对象的UserData就会带有元表了,而元表中的__name字段为"People"字符串。于是我们修改我们上述改名字的例子:
static int InitName(lua_State *L){ // 第 1 步 struct People *pPeople = (struct People *)luaL_testudata(L, 1, “People”); // 第 2 步 if(pPeople != NULL) { pPeople->name = "MaNong"; } return 1;}
我们只是把lua_touserdata改成luaL_testudata函数,函数实现如下,区别在于调用lua_touserdata后,还会再获取它的元表,并判断元表中的__name字段,看是否我们参数中传进来的字符串,若相等才是我们真正期待获取的UserData并进行返回,否则返回NULL:
UserData的讲解到此结束,接下来我们看看LightUserData。
LightUserData
LightUserData是指针。比起(Full)UserData要简单很多,内存管理由交互语言,即上例中C语言管理。我们可以修改例子使用LightUserData如下:
static int People(lua_State *L){ // 对象分配在C语言的堆内存中,并不在Lua中 People *pPeople = new People(); lua_pushlightuserdata(L, pPeople); return 1; // 新的lightuserdata已经在栈上了}
上面函数会创建一个LightUserData对象在Lua中,对象为一个指针,指向一片内存地址,不存储实际的内存数据,上述new方法分配内存的数据存在于C语言部分的堆中。接下来再看修改后的名字设置函数:
static int InitName(lua_State *L){ // 第 1 步 struct People *pPeople = (struct People *)lua_touserdata(L, 1); // 第 2 步 if(pPeople != NULL) { pPeople->name = "MaNong"; } return 1;}
还是使用同样的lua_touserdata函数即可,这里会返回一个指针值,指向C语言堆中的People对象,函数内部会根据类型转化为UserData或LightUserData:
所以我们总结来看,如果C语言结构体对象的内存希望在C语言中进行分配与管理,则可以使用LightUserData,让Lua只存储一个指针值;但若希望对象的内存在Lua中分配与管理,则需要使用UserData。
UserData与LightUserData的讲解到此完结
本文地址:百科问答频道 https://www.neebe.cn/wenda/935433_3.html,易企推百科一个免费的知识分享平台,本站部分文章来网络分享,本着互联网分享的精神,如有涉及到您的权益,请联系我们删除,谢谢!