lua_touserdata

   2023-02-09 学习力0
核心提示:void *lua_touserdata(lua_State*L,intindex);如果给定索引处的值是一个完整的userdata,函数返回内存块的地址。如果值是一个lightuserdata,那么就返回它表示的指针。否则,返回NULL。例如: 在CCLuaStack::executeFunction()函数中有一段代码是用来获取c++
void *lua_touserdata(lua_State*L,intindex);如果给定索引处的值是一个完整的userdata,函数返回内存块的地址。如果值是一个lightuserdata,那么就返回它表示的指针。否则,返回NULL。
例如: 在CCLuaStack::executeFunction()函数中有一段代码是用来获取c++调用lua返回值的。
// get return value
int ret = 0;
if (lua_isnumber(m_state, -1))
{
ret = lua_tointeger(m_state, -1);
}
else if (lua_isboolean(m_state, -1))
{
ret = lua_toboolean(m_state, -1);
}
这个函数只为我们返回了2种类型,如果我们需要返回一些自定义类型(非基本类型),则我们需要这样做:自定义一个返回lua中返回值的函数,在函数中修改上面这段代码加上下面这行代码:
// get return value
int ret = 0;
if (lua_isnumber(m_state, -1))
{
ret = lua_tointeger(m_state, -1);
}
else if (lua_isboolean(m_state, -1))
{
ret = lua_toboolean(m_state, -1);
}
//新添加代码
else if(lua_isuserdata(m_state, -1))
{
ret =  *(XXXX**)lua_touserdata(tolua_s, -1);   //XXXX代表自定义类型
}
至于为什么要这么写->ret =  *(XXXX**)lua_touserdata(tolua_s, -1); 文章一开始就讲清楚了,不懂得恶补一下C++指针(特别是二级指针)的相关知识吧。
1 如何封装c++的指针
对于c++对象的lua包装,我们可以使用
template<typename T>
struct luaUserdataWrapper
{
luaUserdataWrapper() {}
luaUserdataWrapper(const T& d) : data(d) {}
T data; 
};
class CObject
{
public:
int v[10];
};
typedef luaUserdataWrapper<CObject*> luaObject;
这样就可以在c代码中,按照如下方法向lua中添加生成CObject的对象的C函数:
int NewObject( lua_State* L )
{
luaObject* wrapper = (luaObject*) lua_newuserdata( L, sizeof(luaObject) );
wrapper->data = new CObject;
return 1;
}
lua_newuserdata函数把wrapper存放在栈顶位置,作为NewObject的返回值。
wrapper的生存期由lua负责,而wrapper->data的生命期则由程序员自己负责。
在lua代码中的使用方法是:
obj = NewObject() --调用C函数
2 使用metatable
如果此时我们想在lua中使用如下语法:
obj[5]=20
value = obj[5]
则需要我们为luaObject添加metatable属性。
步骤1:
在lua代码中的普通表,不能作为userdata的metatable。必须使用luaL_newmetatable创建的表才能作为userdata的metatable。
在openlib函数中,添加一个userdata 的 metatable表,
int OnOpenlib( lua_State* L )
{
...
luaL_newmetatable( L, “ObjectMetatable");
}
luaL_newmetatable把新创建的表放在栈顶。
注意:新创建的ObjectMetatable表仅在栈中被声明,并没有加入到lua代码中。如果在以后的lua代码中使用ObjectMetatable.__index等操作,会提示ObjectMetatable:a nil value。
步骤2:
这是我们重写上面的New方法。
int NewObject( lua_State* L )
{
luaObject* wrapper = (luaObject*) lua_newuserdata( L, sizeof(luaObject) );
wrapper->data = new CObject;
luaL_getmetatable( L, ”ObjectMetatable“);
lua_setmetatable( L, -2 );
return 1;
}
这样我们就为新生成的luaObject对象添加metatable。
luaL_getmetatable( L, ”ObjectMetatable“)获取ObjectMetatable表,并放入栈顶。
lua_setmetatable( L, -2 )则把新生成的userdata的metatable设置为ObjectMetatable。
步骤3:
value = obj[5]的取下标操作对应的是__index域,而
obj[5]=2;对应的是__newindex域。
所以我们需要添加ObjectMetatable的__index,__newindex域。
我们重写int OnOpenlib( lua_State* L )方法
int OnOpenlib( lua_State* L )
{
...
luaL_newmetatable( L, “ObjectMetatable");
lua_pushstring( L, "__index" );
lua_pushcfunction( L, GetValue );
lua_rawset( L, -3 ); // ObjectMetatable.__index = GetHorizonValue
lua_pushstring( L, "__newindex" );
lua_pushcfunction( L, SetValue );
lua_rawset( L, -3 ); // ObjectMetatable.__newindex = GetHorizonValue
}
GetValue 与SetValue 是自定义的C函数,可以不用被注册到lua代码中。
在lua中调用
v=obj[5]
时,会触发元函数metatable.__index,obj、5会被依次入栈。
所以GetValue方法我们可以写为
int GetValue(lua_State* L)
{
luaL_checktype(L, -1, LUA_TNUMBER);
luaL_checktype(L, -2, LUA_TUSERDATA);
luaObject* wrapper = (luaObject*)   lua_touserdata(L, -2);
ASSERT( wrapper->data != NULL );
if ( wrapper->data == NULL )
{
lua_pushstring( L, "GetHorizonValue: NULL wrapper " );
lua_error(L);
return 1;
}
int index = (int)(float)lua_tonumber(L, -1);
int value = wrapper->data.v[index];
lua_pushnumber( L, value );
return 1;
}
Lua 中 userdata 的反向映射
lua 中,我们可以用 userdata 保存一个 C 结构。当我们为 lua 写扩展时,C 函数中可以利用 lua_touserdata 将 userdata 转换为一个 C 结构指针。
但是,有时候我们却需要把一个指针转换回 lua 中的 userdata 对象。用到它的最常见的地方是封装 GUI ,通常 GUI 的底层是用 C 编码的。当 engine 把鼠标位置或是别的消息拦截到以后,消息会被传递到一个 C 对象中。这个时候,我们需要从 C 对象中得到对应的 lua 对象,并触发事件。
常见的方法是在 C 对象中保留一个 lua 对应对象的 reference , lua 利用注册表中的数字 key 制作了一个简易的 reference 系统。可以让 C 对象保留一个对 lua 中某对象的引用,使得 lua 的 gc 系统不会错误的回收掉它。
效率略高的方法是,直接让 lua 的 userdata 为 C 对象分配内存,这样,可以更直接的利用 lua 的 gc 系统。而我们在 C 中则可以直接保存 userdata 的数据指针。这是有点点 tricky 的方法,因为 lua 并不保证 userdata 分配出来的内存不会因为 gc 而移动。不过我向 lua 作者求证过,在很长一段时间里,lua 都没有计划实现一个带移动的 gc 。(实际上,移动的 gc 会导致大量的 lua 已有代码不能使用)
这样,只要你能保证 userdata 不会被回收,就可以直接保留 userdata 内的数据指针并使用它。
接下来,我们便有了更简单的反向映射方法。就是直接在 lua 的注册表中创建一个弱表,用它来保存指针和 userdata 的 pair 。需要反向映射的时候,直接用指针作为一个 lightuserdata 当 key 去查这张表就可以了。
由于我们把这些映射信息放在一个弱表中,那么也就不需要关心解引用的问题了。
因为我们一定是 C 函数来操作 userdata ,这个映射表完全可以放在 C 函数的 upvalue 或是环境中,而不需要每次都从注册表拿到。
ps. 如果嫌查表的方法得到反向映射对象的效率不高,云风曾经还做过一个 lua api 的扩展,仿造 lua_pushlightuserdata 加了一个 api 为 lua_pushuserdata (就是调整一下内部指针就可以了,避免表查询),可以直接把一个你认为一定是 userdata 的数据指针,转换为 lua object 压回堆栈。不过这个被评价为 unsafe and think local 。好象大家都公认为了一点效率提高而加一个不太安全的 api 没有价值。如果你都需要回调脚本了,在 C 里多做一次表查询对效率的影响已经微乎其微了。
我自己写完了也是觉得意义不大,这里就不贴了。
 
反对 0举报 0 评论 0
 

免责声明:本文仅代表作者个人观点,与乐学笔记(本网)无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
    本网站有部分内容均转载自其它媒体,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责,若因作品内容、知识产权、版权和其他问题,请及时提供相关证明等材料并与我们留言联系,本网站将在规定时间内给予删除等相关处理.

  • LUA解析json小demo
    需要修改的json数据gui-config.json{"configs": [{"server": "JP3.ISS.TF","server_port": 443,"password": "58603228","method": "aes-256-cfb","remarks": ""},{"serv
    03-16
  • 第二十三篇:在SOUI中使用LUA脚本开发界面
    像写网页一样做客户端界面可能是很多客户端开发的理想。做好一个可以实现和用户交互的动态网页应该包含两个部分:使用html做网页的布局,使用脚本如vbscript,javascript做用户交互的逻辑。当需求变化时,只需要在服务端把相关代码调整一下,用户即可看到新的
    03-16
  • windows下编译lua源码"><转>windows下编译lua源
    因为之前一直使用 lua for windows 来搭建lua的使用环境,但是最新的 lua for windows 还没有lua5.2,我又想用这个版本的lua,所以被逼无奈只能自己编一下lua源码。首先从 lua的官网 下载你想要使用的lua源码,比如我下载的就是lua5.2。解压后内容如下:
    03-16
  • lua:使用Lua处理游戏数据
    在之前lua学习:lua作配置文件里,我们学会了用lua作配置文件。其实lua在游戏开发中可以作为一个强大的保存、载入游戏数据的工具。 比如说,现在我有一份表单:data.xls用什么工具解析这个Excel文件并将数据载入游戏?我们可以使用Lua来完成这个工作。不过要
    03-16
  • 第1课 - 学习 Lua 的意义
    第1课 - 学习 Lua 的意义
    第1课 - 学习 Lua 的意义1.Lua 简介           (1) 1993年、巴西(2) 小巧精致的脚本语言,大小只有 200K(3) 用标准C语言写成,能够在所有的平台上编译运行(4) 发明的目标是嵌入在C/C++中,为应用程序提供灵活的扩展和定制功能(5) 不适合用于开发
    03-16
  • RedisTemplate 常用API+事务+陷阱+序列化+pipeline+LUA
    RedisTemplate 常用API+事务+陷阱+序列化+pipel
    https://www.jianshu.com/p/7bf5dc61ca06/https://blog.csdn.net/qq_34021712/article/details/79606551https://www.jianshu.com/p/c9f5718e58f0dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis/artifactId/depe
    03-08
  • Nginx动态路由的新姿势:使用Go取代lua nginx路由规则
    Nginx动态路由的新姿势:使用Go取代lua nginx路
    导语: 在Nitro 中, 我们需要一款专业的负载均衡器。 经过一番研究之后,Mihai Todor和我使用Go构建了基于Nginx、Redis 协议的路由器解决方案,其中nginx负责所有繁重工作,路由器本身并不承载流量。 这个解决方案过去一年在生产环境中运行顺畅。 以下是我
    03-08
  • cocos2d-lua 控制台输入Lua指令方便调试
    用脚本进行开发,如果不能实时去输入指令,就丧失了脚本的一大特色,所以对cocos2d-x程序稍微修改下,使其可以直接从控制台读入lua指令,方便调试。1 首先在行首加入lua的引用,如下1 #include "main.h"2 #include "AppDelegate.h"3 #include "cocos2d.h"4 #i
    02-09
  • Lua 5.2 中文参考手册
    闲来无事,发现Lua更新到了5.2.2,参考手册也更到了5.2,在网上发现只有云风翻译的5.1版,花了几天时间翻译了一些。参考手册有点长,又要随时修改,所以在github上建了项目,有需要的朋友可以看看,同时也欢迎指正。中文手册:Lua 5.2中文参考手册
    02-09
  • lua报错,看到报错信息有tail call,以为和尾调
      尾调用是指在函数return时直接将被调函数的返回值作为调用函数的返回值返回,尾调用在很多语言中都可以被编译器优化, 基本都是直接复用旧的执行栈, 不用再创建新的栈帧, 原理上其实也很简单, 因为尾调用在本质上看的话,是整个子过程调用的最后执行语句,
    02-09
点击排行