1. Lua与C通信,为什么使用虚拟的一个堆栈?
当在 Lua 和 C 之间交换数据有两个问题:
- 动态与静态类型系统的不匹配
- 自动与手动内存管理的不一致
Lua中a[k]=v,a和k可能的类型,有很多种,我们要想映射这个变量可能要写三个参数类型的每一种组合函数。(表、数字、字符串)
C中union
类型能来解决这个问题吗?
如此复杂的类型映射到其它语言可能很困难,
-
Lua 不仅被设计为与
C/C++易于交互,
Java,Fortran 以及类似的语言也一样。
- Lua负责垃圾回收:如果我们将 Lua 值保存在 C 变量中, Lua 引擎没有办法了解这种用法;它可能错误地认为某个值为垃圾并收集他。
所以,不能使用union关键字
替代的方案:
它用一个抽象的栈在 Lua 与 C 之间交换值。栈中的每一条记录都可以保存任何 Lua 值。
2.获取参数的过程:
无论你何时想要从 Lua 请求一个值(比如一个全局变量的值),调用 Lua,被请求的值将会被压入栈。无论你何时想要传递一个值给 Lua,首先将这个值压入栈,然后调用 Lua(这个值将被弹出)。 我们仍然需要一个不同的函数将每种 C 类型压入栈和一个不同函数从栈上取值(注:只是取出不是弹出),但是我们避免了多种不同类型参数的函数组合。
另外,因为栈是由 Lua 来管理的,垃圾回收器知道那个值正在被 C 使用。 几乎所有的 API函数都用到了栈。
3.Lua栈的规则:
Lua 以一个严格的 LIFO 规则(后进先出;也就是说,始终存取栈顶)来操作栈。 当你调用 Lua 时,它只会改变栈顶部分。你的C代码却有更多的自由;更明确的来讲,你可以查询栈上的任何元素,甚至是在任何一个位置插入和删除元素
4.压入元素
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);
//任意的字符串(char*类型,允许包含'\0'字符)用 lua_pushlstring,
//C语言风格(以'\0'结束)的字符串( const char*)用 lua_pushstring:
void lua_pushstring (lua_State *L, const char *s);
Lua 中的字符串不是以零为结束符的;它们依赖于一个明确的长度,因此可以包含
任意的二进制数据。将字符串压入串的正式函数是lua_pushlstring,它要求一个明确的长度作为参数。
-
对于以零结束的字符串,你可以用lua_pushstring(它用
strlen 来计算字符串长度)。
-
Lua 从来不保持一个指向外部字符串(或任何其它对象,除了
C 函数——它总
是静态指针)的指针。
-
对于它保持的所有字符串,Lua
要么做一份内部的拷贝要么重新利用已经存在的字符串。因此,一旦这些函数返回之后你可以自由的修改或是释放你的缓冲区。
5.查询元素
-
API 用索引来访问栈中的元素。在栈中的第一个元素(也就是第一个被压入栈的)
有索引 1,下一个有索引2,以此类推。
-
我们也可以用栈顶作为参照来存取元素,利用负索引。在这种情况下,-1
指出栈顶元素(也就是最后被压入的),-2
指出它的前一个元素,以此类推。
LUA_API int (lua_isnumber)(lua_State *L,int idx);
LUA_API int (lua_isstring)(lua_State *L,int idx);
LUA_API int (lua_iscfunction)(lua_State *L,int idx);
LUA_API int (lua_isuserdata)(lua_State *L,int idx);
int lua_is... (lua_State *L, int index);
//获取值
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_tostring 函数返回一个指向字符串的内部拷贝的指针。你不能修改它(使你想起 那里有一个 const)。只要这个指针对应的值还在栈内,
Lua 会保证这个指针一直有效。
当一个 C 函数返回后, Lua 会清理他的栈,所以,有一个原则:永远不要将指向 Lua 字符串的指针保存到访问他们的外部函数中。
Lua_string 返回的字符串结尾总会有一个字符结束标志
0, 但是字符串中间也可能包 含 0,lua_strlen
返回字符串的实际长度。特殊情况下,假定栈顶的值是一个字符串,下 面的断言(assert)总是有效的:
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);
6.其他堆栈操作
除开上面所提及的 C 与堆栈交换值的函数外, API 也提供了下列函数来完成通常的堆栈维护工作:
//函数 lua_gettop 返回堆栈中的元素个数,它也是栈顶元素的索引。注意一个负数//索引-x 对应于正数索引 gettop-x+1。
int lua_gettop(lua_State *L);
//lua_settop设置栈顶(也就是堆栈中的元素个数)为一个指定的值。如果开始的栈顶高于新的栈顶,顶部的值被丢弃。否则,为了得到指定的大小这个函数压入相应个数的空值//(nil)到栈上。特别的, lua_settop(L,0)清空堆栈。你也可以用负数索引作为调用 lua_settop 的参数;那将会设置栈顶到指定的索引。
//利用这种技巧, API 提供了下面这个宏,它从堆栈中弹出 n 个元素:
//#definelua_pop(L,n) lua_settop(L, -(n)-1)
void lua_settop(lua_State *L, int index);
//函数 lua_pushvalue 压入堆栈上指定索引的一个抟贝到栈顶; lua_remove 移除指定索引位置的元素,并将其上面所有的元素下移来填补这个位置的空白;
void lua_pushvalue(lua_State *L, int index);
void lua_remove(lua_State *L, int index);
void lua_insert(lua_State *L, int index);
void lua_replace(lua_State *L, int index);
lua_insert移动栈顶元素到指定索引的位置,并将这个索引位置上面的元素全部上移至栈顶被移动留下的空隔;最后,lua_replace从栈顶弹出元素值并将其设置到指定索引位置,没有任何移动操作。
注意到下面的操作对堆栈没有任何影响:
lua_settop(L, -1); /* set top to its current value */
lua_insert(L, -1); /* move top element to the top */
demo:
static void stackDump (lua_State *L)
{
int i;
int top = lua_gettop(L);
//从栈底到栈顶遍历了整个堆栈,依照每个元素自己的类型打印出其值。
for (i = 1; i <= top; i++) { /* repeat for each level */
int t = lua_type(L, i);
switch (t)
{
case LUA_TSTRING: /* strings */
printf("`%s'", lua_tostring(L, i));
break;
case LUA_TBOOLEAN: /* booleans */
printf(lua_toboolean(L, i) ? "true" : "false");
break;
case LUA_TNUMBER: /* numbers */
printf("%g", lua_tonumber(L, i));
break;
default: /* other values */
printf("%s", lua_typename(L, t));
break;
}
printf(" "); /* put a separator */
}
printf("\n"); /* end the listing */
}
int _tmain(int argc, _TCHAR* argv[])
lua_State *L = lua_open();
lua_pushboolean(L, 1); lua_pushnumber(L, 10);
lua_pushnil(L); lua_pushstring(L, "hello");
stackDump(L);
/* true 10 nil `hello' */
lua_pushvalue(L, -4); stackDump(L);
/* true 10 nil `hello' true */
lua_replace(L, 3); stackDump(L);
/* true 10 true `hello' */
lua_settop(L, 6); stackDump(L);
/* true 10 true `hello' nil nil */
lua_remove(L, -3); stackDump(L);
/* true 10 true nil nil */
lua_settop(L, -5); stackDump(L);
/* true */
lua_close(L);
return 0;
}
摘自 《Programming in Lua》,内容过多,适当进行了删减 ,加入自己的理解。
|
请发表评论