24.2 堆栈

当在LuaC之间交换数据时我们面临着两个问题:动态与静态类型系统的不匹配和自动与手动内存管理的不一致。

Lua中,我们写下a[k]=v时,kv可以有几种不同的类型(由于metatables的存在,a也可能有不同的类型)。如果我们想在C中提供类似的操作,无论怎样,操作表的函数(settable)必定有一个固定的类型。我们将需要几十个不同的函数来完成这一个的操作(三个参数的类型的每一种组合都需要一个函数)。

我们可以在C中声明一些union类型来解决这个问题,我们称之为lua_Value,它能够描述所有类型的Lua值。然后,我们就可以这样声明settable

void lua_settable (lua_Value a, lua_Value k, lua_Value v);

这个解决方案有两个缺点。第一,要将如此复杂的类型映射到其它语言可能很困难;Lua不仅被设计为与C/C++易于交互,Java,Fortran以及类似的语言也一样。第二,Lua负责垃圾回收:如果我们将Lua值保存在C变量中,Lua引擎没有办法了解这种用法;它可能错误地认为某个值为垃圾并收集他。

因此,Lua API没有定义任何类似lua_Value的类型。替代的方案,它用一个抽象的栈在LuaC之间交换值。栈中的每一条记录都可以保存任何Lua值。无论你何时想要从Lua请求一个值(比如一个全局变量的值),调用Lua,被请求的值将会被压入栈。无论你何时想要传递一个值给Lua,首先将这个值压入栈,然后调用Lua(这个值将被弹出)。我们仍然需要一个不同的函数将每种C类型压入栈和一个不同函数从栈上取值(译注:只是取出不是弹出),但是我们避免了组合式的爆炸(combinatorial explosion)。另外,因为栈是由Lua来管理的,垃圾回收器知道那个值正在被C使用。 几乎所有的API函数都用到了栈。正如我们在第一个例子中所看到的,luaL_loadbuffer把它的结果留在了栈上(被编译的chunk或一条错误信息);lua_pcall从栈上获取要被调用的函数并把任何临时的错误信息放在这里。

Lua以一个严格的LIFO规则(后进先出;也就是说,始终存取栈顶)来操作栈。当你调用Lua时,它只会改变栈顶部分。你的C代码却有更多的自由;更明确的来讲,你可以查询栈上的任何元素,甚至是在任何一个位置插入和删除元素。


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