二、上值
每一次在 Lua 中创建新的 C 函数时,可以将任意数量的上值和这个函数相关联,而每个上值都可以保存一个 Lua 值。后面调用该函数时,可以通过伪索引自由的访问这些上值。这种 C 函数与其上值的关联称为闭包。
上值的个数有限制, C 语言函数中最多可以有 255 个上值,lua_upvalueindex 的最大索引值是 256 。
1、如何为闭包添加上值
这里结合一个例子讲解,我们创建一个 C++ 函数给 Lua 调用,这个 C++ 函数是一个累加的闭包,每次调用都会产生新的值,而且每个闭包的值互不影响。
第一步,创建累加的 C 函数。
函数 newCounter:创建闭包的函数,每次 Lua 中调用 newCounter 函数,都会返回一个新的闭包,会携带新的上值。
函数 counter :真正的执行函数,每次 Lua 执行 newCounter 创建后的闭包都会运行该函数,该函数会 “更新上值以便下次使用” 和 “返回更新后的上值”。
第二步:将上述函数以库的形式添加到 Lua 中
第三步:编写 Lua 文件,并运行
Lua 中创建两个累加器,a 累加器累加三次,b 累加器只累加一次,可以从运行结果看出,两个累加器互不影响。
运行代码
运行结果
2、lua_pushcclosure
描述:
将一个 C 函数作为闭包压入 Lua 栈中。
参数:
· 参数 L: Lua 状态机指针。
· 参数 fn: 一个 C 函数指针,表示要作为闭包压入 Lua 栈的函数。
· 参数 n: 一个整数,表示闭包所使用的上值数量。
3、lua_upvalueindex
描述:
他是一个宏,用于获取闭包的上值在 Lua 栈中的索引。这个索引是一个伪索引,他和其他的栈索引一样,唯一的区别在于它不存在于栈中。
参数:
参数 i : 表示要获取的上值的索引,第一个上值的索引为 1 。
返回值:
返回值是一个整数,表示该上值在 Lua 栈中的索引。
如果传入的索引超出了范围,则会返回 LUA_TNONE 。可以使用 lua_isnone 函数进行判断上值的伪索引是否合法,例如下面代码:
三、共享上值
在封装一个库给 Lua 进行调用的时候,有时需要库函数间共享一些数值或变量。虽然注册表也可以达到这一目的,但不够优雅,毕竟将库的过多细节暴露,并且存在 key 被覆盖的风险,所以更好的做法应该是使用上值。
Lua 为这种场景提供了一种解决方案,简化在库函数间共享上值的做法。
之前我们创建一个 Lua 库时,一般都使用 luaL_newlib 函数,他其实是个宏,具体实现如下所示。
要达到共享上值的关键点在于 luaL_setfuncs 的三个参数,用 luaL_newlib 创建库,默认情况下传递的都是 0 ,即没有共享上值,所以我们只需要改造下就可以支持了。
定义一个库,这个库中有两个函数,共享三个上值,分别为整数型、字符串、table,两个函数均可对共享上值获取和修改
第一步,定义库函数和设置库的共享上值。
showInfo 和 showCode 是两个库函数,会获取各自的参数和共享上值,进行操作后返回给 Lua
共享上值的关键点在 luaopen_user 函数,函数中会压入三个值,然后调用 luaL_setfuncs 将这三个值作为库的共享上值。
第二步,编写 lua 文件。
只是简单的调用库函数,然后打印返回值。
第三步,将库函数添加到 Lua 中,并运行 Lua 文件。
运行结果如下。
四、注册表和上值的区别
注册表提供了全局变量的概念,只要获取到相同的 lua_State ,就能通过 lua_State 访问到相同的注册表,通过对应的 key 获取需要的 value 。
上值则实现了一种只能在特定的函数中可见的机制,相较于注册表能做到更小粒度的共享值。
五、写在最后
Lua 项目地址:Github传送门 (如果对你有所帮助或喜欢的话,赏个star吧,码字不易,请多多支持)
如果觉得本篇博文对你有所启发或是解决了困惑,点个赞或关注我呀。
公众号搜索 “江澎涌”,更多优质文章会第一时间分享与你。