27.3.3 Upvalues |
registry 实现了全局的值,upvalue机制实现了与C static变量等价的东东,这种变量只能在特定的函数内可见。每当你在Lua中创建一个新的C函数,你可以将这个函数与任意多个upvalues联系起来,每一个upvalue 可以持有一个单独的Lua值。下面当函数被调用的时候,可以通过假索引自由的访问任何一个upvalues。
我们称这种一个C函数和她的upvalues的组合为闭包(closure)。记住:在Lua代码中,一个闭包是一个从外部函数访问局部变量的函数。一个C闭包与一个Lua闭包相近。关于闭包的一个有趣的事实是,你可以使用相同的函数代码创建不同的闭包,带有不同的upvalues。
看一个简单的例子,我们在C中创建一个newCounter函数。(我们已经在6.1节部分在Lua中定义过同样的函数)。这个函数是个函数工厂:每次调用他都返回一个新的counter函数。尽管所有的counters共享相同的C代码,但是每个都保留独立的counter变量,工厂函数如下:
/* 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),第三个参数是upvalues的个数(例子中为1)。在创建新的闭包之前,我们必须将upvalues的初始值入栈,在我们的例子中,我们将数字0作为唯一的upvalue的初始值入栈。如预期的一样,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闭包不能共享upvalues:每一个闭包都有自己独立的变量集。然而,我们可以设置不同函数的upvalues指向同一个表,这样这个表就变成了一个所有函数共享数据的地方。