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指向同一个表,这样这个表就变成了一个所有函数共享数据的地方。

 

 

 



相关链接:
lua程序设计目录 - 中国lua开发者 - lua论坛