lua闭包的理解:
http://www.cnblogs.com/ringofthec/archive/2010/11/05/luaClosure.html
1. 理解lua的栈到底是什么?
lua的栈类似于以下的定义, 它是在创建lua_State的时候创建的:
[cpp] view plaincopyprint?
TValue stack[max_stack_len] // 欲知内情可以查 lstate.c 的stack_init函数
TValue stack[max_stack_len] // 欲知内情可以查 lstate.c 的stack_init函数
存入栈的数据类型包括数值, 字符串, 指针, talbe, 闭包等, 下面是一个栈的例子:
lua栈
执行下面的代码就可以让你的lua栈上呈现图中的情况
[cpp] view plaincopyprint?
lua_pushcclosure(L, func, 0) // 创建并压入一个闭包
lua_createtable(L, 0, 0) // 新建并压入一个表
lua_pushnumber(L, 343) // 压入一个数字
lua_pushstring(L, “mystr”) // 压入一个字符串
lua_pushcclosure(L, func, 0) // 创建并压入一个闭包 lua_createtable(L, 0, 0) // 新建并压入一个表 lua_pushnumber(L, 343) // 压入一个数字 lua_pushstring(L, “mystr”) // 压入一个字符串
[cpp] view plaincopyprint?
堆栈的序号可以从栈顶和栈底计数,从栈底计数,则栈底是1,向栈顶方向递增。从栈顶计数,则栈顶是-1,向栈底方向递减。一般都用从栈顶计数的方式。堆栈的默认大小是20,可以用lua_checkstack修改.用lua_gettop则可以获得栈里的元素数目。并不是说在栈顶有一个整形元素。而是计算了一下栈顶元素在栈里的正index,相当于元素数目。
堆栈的序号可以从栈顶和栈底计数,从栈底计数,则栈底是1,向栈顶方向递增。从栈顶计数,则栈顶是-1,向栈底方向递减。一般都用从栈顶计数的方式。堆栈的默认大小是20,可以用lua_checkstack修改.用lua_gettop则可以获得栈里的元素数目。并不是说在栈顶有一个整形元素。而是计算了一下栈顶元素在栈里的正index,相当于元素数目。这里要说明的是, 你压入的类型有数值, 字符串, 表和闭包[在c中看来是不同类型的值], 但是最后都是统一用TValue这种数据结构来保存的:), 下面用图简单的说明一下这种数据结构:
lua
TValue结构对应于lua中的所有数据类型, 是一个{值, 类型} 结构, 这就lua中动态类型的实现, 它把值和类型绑在一起, 用tt记录value的类型, value是一个联合结构, 由Value定义, 可以看到这个联合有四个域, 先说明简单的
p -- 可以存一个指针, 实际上是lua中的light userdata结构
n -- 所有的数值存在这里, 不过是int , 还是float
b -- Boolean值存在这里, 注意, lua_pushinteger不是存在这里, 而是存在n中, b只存布尔
gc -- 其他诸如table, thread, closure, string需要内存管理垃圾回收的类型都存在这里
gc是一个指针, 它可以指向的类型由联合体GCObject定义, 从图中可以看出, 有string, userdata, closure, table, proto, upvalue, thread
从下面的图可以的得出如下结论:
1. lua中, number, boolean, nil, light userdata四种类型的值是直接存在栈上元素里的, 和垃圾回收无关.
2. lua中, string, table, closure, userdata, thread存在栈上元素里的只是指针, 他们都会在生命周期结束后被垃圾回收.
2. lua和c通信的约定
lua和c通信时有这样的约定: 所有的lua中的值由lua来管理, c++中产生的值lua不知道, 类似表达了这样一种意思: "如果你(c/c++)想要什么, 你告诉我(lua), 我来产生, 然后放到栈上, 你只能通过api来操作这个值, 我只管我的世界", 这个很重要, 因为:
"如果你想要什么, 你告诉我, 我来产生"就可以保证, 凡是lua中的变量, lua要负责这些变量的生命周期和垃圾回收, 所以, 必须由lua来创建这些值(在创建时就加入了生命周期管理要用到的簿记信息)
"然后放到栈上, 你只能通过api来操作这个值", lua api给c提供了一套完备的操作界面, 这个就相当于约定的通信协议, 如果lua客户使用这个操作界面, 那么lua本身不会出现任何"意料之外"的错误.
"我只管我的世界"这句话体现了lua和c/c++作为两个不同系统的分界, c/c++中的值, lua是不知道的, lua只负责它的世界
3. lua value 和 c value的对应关系
c lua
nil 无 {value=0, tt = t_nil}
boolean int 非0, 0 {value=非0/0, tt = t_boolean}
number int/float等 1.5 {value=1.5, tt = t_number}
lightuserdata void*, int*, 各种* point {value=point, tt = t_lightuserdata}
string char str[] {value=gco, tt = t_string} gco=TString obj
table 无 {value=gco, tt = t_table} gco=Table obj
userdata 无 {value=gco, tt = t_udata} gco=Udata obj
closure 无 {value=gco, tt = t_function} gco=Closure obj
可以看出来, lua中提供的一些类型和c中是对应的, 也提供一些c中没有的类型. 其中有一些药特别的说明一下:
nil值, c中没有对应, 但是可以通过lua_pushnil向lua中压入一个nil值
注意: lua_push*族函数都有"创建一个类型的值并压入"的语义, 因为lua中所有的变量都是lua中创建并保存的, 对于那些和c中有对应关系的lua类型, lua会通过api传来的附加参数, 创建出对应类型的lua变量放在栈顶, 对于c中没有对应类型的lua类型, lua直接创建出对应变量放在栈顶.
例如: lua_pushstring(L, “string”) lua根据"string"创建一个 TString obj, 绑定到新分配的栈顶元素上
lua_pushcclosure(L,func, 0) lua根据func创建一个 Closure obj, 绑定到新分配的栈顶元素上
lua_pushnumber(L,5) lua直接修改新分配的栈顶元素, 将5赋值到对应的域
lua_createtable(L,0, 0)lua创建一个Tabke obj, 绑定到新分配的栈顶元素上
总之, 这是一个 c value –> lua value的流向, 不管是想把一个简单的c数据放入lua的世界, 还是创建一个table, 都会导致
1. 栈顶新分配元素 2. 绑定或赋值
还是为了重复一句话, 一个c value入栈就是进入了lua的世界, lua会生成一个对应的结构并管理起来, 从此就不再依赖这个c value
lua value –> c value时, 是通过 lua_to* 族api实现, 很简单, 取出对应的c中的域的值就行了, 只能转化那些c中有对应值的lua value, 比如table就不能to c value, 所以api中夜没有提供 lua_totable这样的接口.
本资料整理自网络!
--------------------------------------------------------------------------------------------------------
这些东西是平时遇到的, 觉得有一定的价值, 所以记录下来, 以后遇到类似的问题可以查阅, 同时分享出来也能方便需要的人, 转载请注明来自RingOfTheC[ring.of.the.c@gmail.com]
1. 建一个新表
void lua_createtable (lua_State *L, int narr, int nrec)创建一个新的table, 并把它放在栈顶. narr和nrec分别指定该table的array部分和hash部分的预分配元素数量无返回值栈高度+1, 栈顶元素是新table#define lua_newtable(L) lua_createtable(L, 0, 0) 常用这个 2. 取表中的元素void lua_getfield (lua_State *L, int index, const char *k)操作: arr = Stack[index] // arr肯定是表 Stack.push( arr[k] )取表中键为k的元素, 这里的表是由index指向的栈上的一个表无返回值栈高度+1, 栈顶元素是(Stack[index])[k]注意, 该操作将触发 __index 元方法 3. 给表中的元素赋值void lua_setfield (lua_State *L, int index, const char *k)操作: arr = Stack[index] arr[k] = Stack.top() Stack.pop()给表中键为k的元素赋值value(value就是栈顶元素), 这里的表是由index指向的栈上的一个表无返回值栈高度-1, 被弹出的是value注意, 该操作将触发 __newindex 元方法 4. 取表元素 和 表元素赋值void lua_gettable (lua_State *L, int index)操作: ele = Stack[index]
key = Stack.top()
Stack.pop()
value = ele[key]
Stack.push(value)
根据index指定取到相应的表; 取栈顶元素为key, 并弹出栈; 获取表中key的值压入栈顶.
无返回值
栈高度不变, 但是发生了一次弹出和压入的操作, 弹出的是key, 压入的是value
注意, 该操作将触发 __index 元方法
void lua_settable (lua_State *L, int index)操作: ele = Stack[index] value = Stack.top() Stack.pop() key = Stack.top() Stack.pop() ele[key] = value根据index指定取到相应的表; 取栈顶元素做value, 弹出之; 再取当前栈顶元素做key, 亦弹出之; 然后将表的键为key的元素赋值为value无返回值栈高度-2, 第一次弹出value, 第二次弹出key注意, 该操作将触发 __newindex 元方法 5. 对table的一些操作[不引发原方法]void lua_rawget (lua_State *L, int index)和lua_gettable操作一样
但是不触发相应的元方法
void lua_rawgeti(lua_State *L, int index, int n)
操作: ele = Stack[index]
value = ele[n]
Stack.push(value)
无返回值
栈+1, 栈顶新增元素就是 value
不触发相应的元方法
void lua_rawset (lua_State *L, int index) 和lua_settable操作一样
但是不触发相应的原方法
void lua_rawseti (lua_State *L, int index, int n) 操作: ele = Stack[index]
value = Stack.top()
Stack.pop()
ele[n] = value
无返回值
栈-1, 栈顶将value弹出
不触发相应的元方法
6. 复制栈上元素并压入栈
void lua_pushvalue (lua_State *L, int index)操作: value = Stack[index]
Stack.push(value)
无返回值
栈+1
7. 创建一个元表
int luaL_newmetatable (lua_State *L, const char *tname)操作: 1. 在注册表中查找tname, 如果已经注册, 就返回0, 否者继续, 并平栈
lua_getfield(L, LUA_REGISTRYINDEX, tname)
if (!lua_isnil(L, -1))
return 0;
lua_pop(L, 1);
2. 创建一个表, 并注册, 返回1
lua_newtable(L)
lua_pushvalue(L, -1)
lua_setfield(L, LUA_REGISTRYINDEX, tname)
return 1
有返回值栈+1, 栈顶元素是在注册表中注册过的新表 8. 创建C值void *lua_newuserdata (lua_State *L, size_t size)该函数分配一块由size指定大小的内存块, 并放在栈顶
返回值是新分配的块的地址
栈+1, 栈顶是userdata
userdata用来在lua中表示c中的值. 一个完整的userdata有自己的元表, 在垃圾回收时, 可以调用它的元表的__gc方法
9. 注册c函数到lua中, 其实没有这回事, lua中只有c闭包
void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n)向栈上压一个C闭包
当一个c函数被创建时, 可以绑定几个值在它上面, 从而形成一个闭包. 在任何时刻调用这个c函数时, 都可以访问这几个绑定值.
绑定的方法: 先一次压入要绑定的n个值到栈上, 然后调用lua_pushcclosure(L, fn, n)这样就形成的一个c闭包
无返回值
栈 –(n - 1) , 一共弹出n个元素(及那些绑定的值), 压入一个cclosure
#define lua_pushcfunction(L, f) lua_pushcclosure(L, f, 0)
#define lua_register(L, n, f) (lua_pushcfunction(L, f), lua_setglobal(L, n))
没有返回值
栈不变化
这个是比较常用的, 以n为lua中的key压入一个0个绑定值的cclosure.
--------------------------------------------------------------------------------------------------------
这次主要记录lua函数调用的几个相关的api, 需要说明的是, 对于这几个api, lua手册上写的相当详细, 相当好, 可以直接看手册
10. 调用一个lua函数
void lua_call(lua_State* L, int nargs, int nresults)
lua c api的特点就是"不是一个人在战斗" [我想表达的意思是, lua中的一句话, 在c api实现起来就是n句, 可能有人疑惑那为什么不直接用lua多好, c api这么麻烦, 答案是有的事只能用c api才能实现], 所以, 调用它之前, 需要布局一下栈, 第一, 要把要call的函数压入栈; 第二, call要用的参数正序压入栈中; 然后才能调用lua_call, 调用完了, 自己去取返回值, 它都给你压栈上了.
操作:
argn = Stack.pop()
... // 一共压入nargs个参数
arg2 = Stack.pop()
arg3 = Stack.pop()
func = Stack.pop() // 函数本身也弹出
res1, res2, ..., resj = func(arg1, arg2, ..., argn)
Stack.push(res1)
Stack.push(res2)
… // 压入nresults个返回值
Stack.push(resj)
无返回值
调用结束后, 栈高度增加 nresults – (1 + nargs), 如果将nresults参数设置为LUA_MULTRET, 那么lua返回几个值, 栈上就压入几个值, 否者强制压入nresults个值, 不足的是空值, 多余的抛弃掉
注意, 这个函数是有危险的, 如果在其中发生了错误, 会直接退出程序
这个函数的用途: 尚未发现, 除非你能接受出错立马退出, 反正我是做游戏的, 我受不起, 呵呵, 顺便一说, lauxlib.h中的luaL_check*一族函数也是这样的, 不符合预期的话, 直接退出, 这些函数都要小心, 有类似于断言的效果.
11. 保护下调用一个lua函数
int lua_pcall(lua_State* L, int nargs, int nresults, int errfunc)
参数, 行为和lua_call都一样, 如果在调用中没有发生任何错误, lua_pcall == lua_call; 但是如果有错误发生时, lua_pcall会捕获它
errfunc指出了Stack上的一个元素, 这个元素应该是一个函数, 当发生错误的时候
ef = Stack[errfunc]
value = ef(errmsg)
Stack.push(value)
也就是说, 在错误的时候, errfunc指定的错误处理函数会被调用, 该处理函数的返回值被压到栈上.
默认情况下, 可以给errfunc传值0, 实际的效果是指定了这样一个函数做出错处理 function defaulterr(errmsg) return errmsg end.
本函数有返回值 LUA_ERRRUN运行时错误 LUA_ERRMEM内存分配错误[注意, 这种错会导致lua调用不了错误处理函数] LUA_ERRERR运行错误处理函数时出错了, 写程序的时候必须检查返回值:)
强烈推荐该函数, 不过事实上大家也都用的这个函数:)
12. 保护下调用一个c函数
int lua_cpcall (lua_State *L, lua_CFunction func, void *ud)以保护模式调用c函数, func中可以且只能从堆栈上拿到一个参数, 就是ud, 当有错误时, 和lua_pcall返回相同的错误代码, 并在堆栈顶部留下errmsg字符串, 调用成功的话它返回零, 并且不会修改堆栈, 所有从func中返回的值都被扔掉.
这里注意的问题是:
1. "当有错误时", 这个错误的意思是lua的错误, 而不是c/c++的错误. 在func中使用lua_call和lua_check*族函数, 并不会导致程序退出了, 而是表现的像lua_pcall那样.
2. 调用成功的时候func中的返回值都被扔掉了.
分享到:
相关推荐
这使得C++和lua之间可以自由地交换数据。 5. **处理错误和异常**:Kaguya提供了异常处理机制,当lua脚本执行出错时,会抛出一个异常。你可以使用try-catch语句捕获并处理这些异常。 6. **lua对象和C++对象的交互**...
使用lua_push*和lua_to*系列函数进行类型转换,例如lua_pushnumber将C++的double转换为Lua的数值,lua_tostring则将Lua的字符串转回C++的char*。 4. **错误处理**:在交互过程中,必须注意错误处理。lua_pcall函数...
这个封装库的主要目标是简化C++与Lua之间的数据交互,提供一种高效且直观的方式来调用Lua函数,创建和操作Lua对象,并将C++类型映射到Lua上。 在C++程序中使用Lua的主要好处在于,Lua作为一个轻量级的脚本语言,...
6. 传递和接收数据:使用`lua_pushXXX`和`lua_toXXX`在C++与LUA之间交换数据。 7. 错误处理:在调用`lua_pcall`时,检查返回值以捕获错误。 8. 关闭LUA状态机:完成交互后,记得使用`lua_close`关闭`lua_State`。 ...
在`lua-5.2.3`这个版本中,Lua引入了一些新的特性和改进,比如元表的改进、协程的增强等,使得它在处理复杂逻辑和数据结构时更加灵活。 在C++项目中集成Lua,我们需要使用Lua的C API。这是一组用于与Lua交互的函数...
在C++中集成Lua脚本语言,主要是为了利用其轻量级、易嵌入和高效的特点,以增强软件的灵活性和可扩展性。...通过深入理解和实践Lua的API,C++开发者可以充分利用这种融合的优势,创造出更加丰富和动态的应用程序。
首先,理解C++与Lua交互的核心是Lua的C API。Lua提供了一套C接口,使得C/C++代码能够直接调用Lua的函数,注册C++函数到Lua环境,甚至创建和操作Lua对象。在"C++的lua访问封装"这个主题中,我们将重点讨论如何构建一...
在IT行业中,数据交换和处理是一项常见的任务,尤其是在游戏开发和配置管理中。本文将详细介绍如何使用C++来读取Excel 2007(.xlsx)文件,并将其转换为Lua脚本文件。首先,我们需要了解Excel 2007采用的OpenXML格式...
5. **数据交换**:C/C++与Lua之间可以互相传递数据。`lua_push*()`系列函数将C类型的数据转换为Lua值,`lua_to*()`系列函数则将Lua值转换回C类型。 6. **错误处理**:在调用Lua API时,要时刻检查返回值,确保没有...
在游戏运行过程中,C++和Lua之间的数据交换可能通过调用函数、共享数据结构等方式实现。 7. **调试与日志**:为了理解交互过程,开发者可能需要查看和分析运行时的日志,了解C++和Lua之间数据传递的情况,以及可能...
在 Lua 语言中,C/C++ 代码与 Lua 脚本进行交互是通过 Lua 提供的 C API 完成的,其中栈(Stack)扮演着至关重要的角色。栈是 Lua 与 C 之间传递数据的主要媒介。当你调用 `lua_open` 初始化一个新的 Lua 环境时,栈...
C/C++与Lua之间可以通过栈进行数据交换。Lua的C API提供了一系列函数,如`lua_push*()`和`lua_get*()`,用于在C/C++和Lua之间传递不同类型的数据,包括整数、浮点数、字符串、表等。 7. **回调机制** C/C++函数...
6. **数据交换**:Lua和C++之间的数据交换通常通过栈操作实现。你可以使用lua_push*()系列函数将C++的数据压入栈,然后在Lua脚本中使用;反之,使用lua_to*()函数从栈中取出数据。 7. **错误处理**:在执行Lua代码...
6. **数据交换**:Lua和C++之间可以共享数据。Lua可以访问C++的对象,反之亦然,但需要注意类型转换和内存管理的问题。 在《Lua脚本在C++下的舞步》系列中,可能详细讲解了以上各个步骤,包括实例演示和最佳实践。...
在IT领域,C++与LUA的交互是一个常见的技术需求,特别是在游戏开发、自动化脚本以及...通过理解LUA的状态机、C API、数据交换机制以及错误处理,开发者可以顺利地在C++程序中集成和执行LUA脚本,实现更复杂的功能。
- 在C++中定义一个函数,如`lua_encrypt`和`lua_decrypt`,它们接受Lua传来的参数(如文件路径、密钥等),调用相应的加密解密API,然后将结果返回给Lua。 - 使用`lua_pushstring`将结果字符串推到Lua的堆栈上,...
这些API函数的使用让C/C++代码能够与Lua环境进行通信,实现函数调用和数据交换。掌握这些知识后,我们就可以编写C/C++扩展,供Lua脚本调用,或者在C/C++程序中嵌入Lua解释器,执行Lua脚本来完成特定功能。这对于游戏...
此外,`lua_gettop`、`lua_pushnumber` 和 `lua_pcall` 等函数提供了在 C 代码中与 Lua 脚本进行数据交换和调用 Lua 函数的能力。 在 aarch64 平台上,开发者需要注意一些特定的架构问题,比如内存对齐、指令集的...
4. 数据交互:Lua和C++之间的数据交换是通过Lua的栈机制实现的。你可以使用lua_push*系列函数将C++的数据类型转换为Lua值,再使用lua_to*系列函数将Lua值转换回C++类型。 5. 错误处理:Lua运行时可能出现错误,需要...
它可能包括了创建Lua状态机,加载一个简单的Lua脚本(比如执行一些计算或操作),以及如何在C++和Lua之间交换数据。这个例子对于理解如何在实际项目中使用Lua作为扩展语言非常有帮助。 总结一下,本示例的核心知识...