lua_touserdata

lua_touserdata,第1张

概述void *lua_touserdata(lua_State*L,intindex);如果给定索引处的值是一个完整的userdata,函数返回内存块的地址。如果值是一个lightuserdata,那么就返回它表示的指针。否则,返回NULL。 例如: 在CCLuaStack::executeFunction()函数中有一段代码是用来获取c++调用lua返回值的。 // get return valu 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); } //新添加代码 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 里多做一次表查询对效率的影响已经微乎其微了。 我自己写完了也是觉得意义不大,这里就不贴了。 总结

以上是内存溢出为你收集整理的lua_touserdata全部内容,希望文章能够帮你解决lua_touserdata所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/1259023.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-07
下一篇 2022-06-07

发表评论

登录后才能评论

评论列表(0条)

保存