kenlistian

勤学多思

  IT博客 :: 首页 :: 新随笔 ::  :: 聚合  :: 管理 ::
  412 随笔 :: 0 文章 :: 23 评论 :: 0 Trackbacks
为何起用lua堆栈

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

在Lua中,我们写下a[k]=v时,k和v可以有几种不同的类型(由于metatables的存在,a也可能有不同的类型)。

如果我们想在C中提供类似的操作,无论怎样,操作表的函数(settable)必定有一个固定的类型。
我们将需要几十个不同的函数来完成这一个的操作(三个参数的类型的每一种组合都需要一个函数)。

在C中声明一些union类型来解决这个问题,称之为lua_Value,它能够描述所有类型的Lua值。
,就可以这样声明settable
void lua_settable (lua_Value a, lua_Value k, lua_Value v);
这个解决方案有两个缺点。
1。要将如此复杂的类型映射到其它语言可能很困难;Lua不仅被设计为与C/C++易于交互,
  Java,Fortran以及类似的语言也一样。
2。Lua负责垃圾回收:如果将Lua值保存在C变量中,Lua引擎没有办法了解这种用法;
   它可能错误地认为某个值为垃圾并收集他。
       
Lua API没有定义任何类似lua_Value的类型。替代的方案,
它用一个抽象的栈在Lua与C之间交换值。
栈中的每一条记录都可以保存任何Lua值。

无论你何时想要从Lua请求一个值(比如一个全局变量的值),
1.将这个值压入栈,
2。调用Lua(这个值将被弹出)。

仍然需要一个不同的函数将每种C类型压入栈和一个不同函数从栈上取值(译注:只是取出不是弹出),
但是我们避免了组合式的爆炸(combinatorial explosion)。另外,因为栈是由Lua来管理的,
垃圾回收器知道那个值正在被C使用。几乎所有的API函数都用到了栈。

lua_pcall从栈上获取要被调用的函数并把任何临时的错误信息放在这里。
Lua以一个严格的LIFO规则(后进先出;也就是,始终存取栈顶)来操作栈。

当你调用Lua时,它只会改变栈顶部分。你的C代码却有更多的自由;更明确的来讲,
你可以查询栈上的任何元素,甚至是在任何一个位置插入和删除元素。
      
API有一系列压栈的函数,它将每种可以用C来描述的Lua类型压栈:
空值(nil)                                   用lua_pushnil,
数值型(doubele)                             用lua_pushnumber,
布尔型(在C中用整数表示)                     用lua_pushboolean,
任意的字符串(char*类型,允许包含'\0'字符)   用lua_pushlstring,
C语言风格(以'\0'结束)的字符串(const char*)用lua_pushstring:

void lua_pushnil (lua_State *L);
void lua_pushboolean (lua_State *L, int bool);
void lua_pushnumber (lua_State *L, double n);
void lua_pushlstring (lua_State *L, const char *s,size_t length);
void lua_pushstring (lua_State *L, const char *s);

同样也有将C函数和userdata值压入栈的函数,稍后会讨论到它们。
Lua中的字符串不是以零为结束符的;它们依赖于一个明确的长度,
因此可以包含任意的二进制数据。

将字符串压入串的正式函数是lua_pushlstring,它要求一个明确的长度作为参数。
对于以零结束的字符串,你可以用lua_pushstring(它用strlen来计算字符串长度)。

Lua是拷贝字符串而不是保持指向外部字符串(或任何其它对象,除了C函数——它总是静态指针)的指针。
对于它保持的所有字符串,Lua要么做一份内部的拷贝要么重新利用已经存在的字符串。

检测栈上是否有足够你需要的空间
int lua_checkstack (lua_State *L, int sz);


查询元素
  API用索引来访问栈中的元素。
 
  在栈中的第一个元素(也就是第一个被压入栈的)有索引1,下一个有索引2,以此类推。
  我们也可以用栈顶作为参照来存取元素,利用负索引。
  在这种情况下,-1指出栈顶元素(也就是最后被压入的),-2指出它的前一个元素,以此类推。
 
  如,调用lua_tostring(L, -1)以字符串的形式返回栈顶的值。
 
  在某些场合使用正索引访问栈较方便,另外一些情况下,使用负索引访问栈更方便。

API提供了一套lua_is*函数来检查一个元素是否是一个指定的类型,*可以是任何Lua类型。
因此有lua_isnumber,lua_isstring,lua_istable以及类似的函数。所有这些函数都有同样
的原型:

int lua_is... (lua_State *L, int index);
lua_isnumber和lua_isstring函数不检查这个值是否是指定的类型,而是看它是否能被转
换成指定的那种类型。例如,任何数字类型都满足lua_isstring。
还有一个lua_type函数,它返回栈中元素的类型。
(lua_is*中的有些函数实际上是用了这个函数定义的宏)

为了从栈中获得值,这里有lua_to*函数:
int lua_toboolean (lua_State *L, int index);
double lua_tonumber (lua_State *L, int index);
const char * lua_tostring (lua_State *L, int index);
size_t lua_strlen (lua_State *L, int index);
即使给定的元素的类型不正确,调用上面这些函数也没有什么问题。在这种情况下,
lua_toboolean、lua_tonumber和lua_strlen返回0,其他函数返回NULL。

由于ANSI C没有提供有效的可以用来判断错误发生数字值,所以返回的0是没有什么用处的。
对于其他函数而言,我们一般不需要使用对应的lua_is*函数:我们只需要调用lua_is*,
测试返回结果是否为NULL即可。

//返回的是一个指针,则需要另外找个地址拷贝值。。。。。
Lua_tostring函数返回一个指向字符串的内部拷贝的指针。你不能修改它(使你想起那里有一个const)。
只要这个指针对应的值还在栈内,Lua会保证这个指针一直有效。

当一个C函数返回后,Lua会清理他的栈,所以,有一个原则:永远不要将指向Lua字符串的指针
保存到访问他们的外部函数中。

Lua_string返回的字符串结尾总会有一个字符结束标志0,但是字符串中间也可能包含0,
lua_strlen返回字符串的实际长度。特殊情况下,假定栈顶的值是一个字符串,

下面的断言(assert)总是有效的:

//s是一个指向某个字符串地址的指针
const char *s = lua_tostring(L, -1); // any Lua string
size_t l = lua_strlen(L, -1);        // its length
assert(s[l] == '\0');
assert(strlen(s) <= l);



posted on 2008-09-29 11:25 kenlistian 阅读(3089) 评论(0)  编辑 收藏 引用 所属分类: AI
只有注册用户登录后才能发表评论。