第一版是为 Lua 5.0 编写的。虽然在很大程度上仍然适用于更高版本,但有一些差异。
第四版针对 Lua 5.3,可在 亚马逊 和其他书店购买。
购买本书,您还可以帮助 支持 Lua 项目。
用 Lua 编程 | ||
第四部分 C API 第 27 章 编写 C 函数的技术 |
注册表实现全局值,而上值机制实现 C 静态变量的等价项,这些变量仅在特定函数内可见。每次在 Lua 中创建新的 C 函数时,都可以将任意数量的上值与其关联;每个上值可以保存一个 Lua 值。稍后,当调用该函数时,它可以使用伪索引自由访问其任何上值。
我们将 C 函数与其上值的这种关联称为闭包。请记住,在 Lua 代码中,闭包是一个使用来自外部函数的局部变量的函数。C 闭包是对 Lua 闭包的 C 近似。关于闭包的一个有趣的事实是,可以使用相同的函数代码创建不同的闭包,但使用不同的上值。
为了查看一个简单的示例,让我们在 C 中创建一个 newCounter
函数。(我们已经在 Lua 中定义了这个相同的函数,请参见 第 6.1 节。)此函数是一个工厂函数:每次调用它时,它都会返回一个新的计数器函数。虽然所有计数器共享相同的 C 代码,但每个计数器都保持自己的独立计数器。工厂函数如下所示
/* forward declaration */ static int counter (lua_State *L); int newCounter (lua_State *L) { lua_pushnumber(L, 0); lua_pushcclosure(L, &counter, 1); return 1; }此处的关键函数是
lua_pushcclosure
,它创建一个新的闭包。它的第二个参数是基本函数(示例中的 counter
),第三个参数是上值的数量(示例中的 1)。在创建新的闭包之前,我们必须将上值的初始值压入堆栈。在我们的示例中,我们将数字 0 作为单个上值的初始值进行推送。正如预期的那样,lua_pushcclosure
将新的闭包留在堆栈上,因此闭包已准备好作为 newCounter
的结果返回。
现在,让我们看看 counter
的定义
static int counter (lua_State *L) { double val = lua_tonumber(L, lua_upvalueindex(1)); lua_pushnumber(L, ++val); /* new value */ lua_pushvalue(L, -1); /* duplicate it */ lua_replace(L, lua_upvalueindex(1)); /* update upvalue */ return 1; /* return new value */ }在此,关键函数是
lua_upvalueindex
(实际上是一个宏),它生成一个 upvalue 的伪索引。同样,这个伪索引就像任何栈索引,除了它不在栈中。表达式 lua_upvalueindex(1)
指示函数的第一个 upvalue 的索引。因此,函数 counter
中的 lua_tonumber
将第一个(也是唯一一个)upvalue 的当前值作为数字检索。然后,函数 counter
推入新值 ++val
,复制它,并使用其中一个副本用新值替换 upvalue。最后,它将另一个副本作为其返回值返回。
与 Lua 闭包不同,C 闭包无法共享 upvalue:每个闭包都有自己独立的集合。但是,我们可以设置不同函数的 upvalue 以引用一个公共表,这样该表就成为这些函数可以共享数据的公共场所。
版权所有 © 2003–2004 Roberto Ierusalimschy。保留所有权利。 |