您的位置:首页 > 编程语言 > Lua

Lua程序设计笔记之六: C API的使用

2013-07-15 23:27 323 查看
一. C API
C API是一组能使C代码与Lua交互的函数,其中包括读写Lua全局变量,调用Lua函数,运行一段Lua代码,以及注册C函数以供Lua代码调用等。C API遵循C语言的操作模式(modus operandi),这与lua的操作模式大为不同,当在C语言中编程时,应该注意类型检测(和类型错误),错误恢复、内存分配错误和其他的源代码复杂性。
Lua和C语言通信的主要方式是一个无所不在的虚拟栈,几乎所有的API调用都会操作这个栈上的值。所有的数据交换,无论是lua还是C语言或C语言到Lua都通过这个栈完成。此外,还可以用这个栈来保存一些中间结果。栈可以解决Lua和C语言之间存在的两大差异,第一种差异是Lua使用垃圾收集,而C语言要求显示地释放内存;第二种是Lua使用动态类型,而C语言使用静态类型。
1. 栈:Lua API使用了一个抽象的栈,在Lua和C语言之间交换数据,栈中的每个元素都能保存任何类型的Lua值。对于每个类型的值,使用了一系列特定类型的操作函数。
2. 压入元素:
对于与每种可以呈现在lua中的C类型,API都有一个对应的压入函数:
void lua_pushnil (lua_State * L);
void lua_pushboolean (lua_State * L, int bool);
void lua_pushnumber (lua_State * L, lua_Number n);
void lua_pushinteger (lua_State * L, lua_Integer n);
void lua_pushlstring (lua_State * L, const char * s, size_t len)
void lua_pushstring (lua_State * L, const char * s);
向栈中压入一个元素时,应该确保栈中具有足够的空间。当lua启动时,或lua调用C语言时,栈中至少会有20个空闲的槽。对于特殊情况,需要检查栈中空间是否够用:
int lua_checkstack (lua_State * L , int sz)
3. 查询元素:
1)API使用"索引"来引用栈中的元素,第一个压入栈中的是1,第二个压入的是2,依此类推;还有一种是可以以栈顶为参考物,使用负数的索引来访问栈中的元素,比如-1表示栈顶。
2)API提供了一系列的函数lua_is*,来检查某一个元素是否为特定的类型,*可以是任意的Lua类型那个。此类函数的原型是:int lua_is* (lua_State *L, int index)
3) 函数lua_type用来返回栈中元素的类型,每种类型都对应一个常量,在lua.h头文件中,LUA_TNIL, LUA_TBOOLEAN, LUA_TNUMBER, LUA_TSTRING, LUA_TTABLE,LUA_TTHREAD,LUA_TUSERDATA和LUA_TFUNCTION。
4) lua_to*函数用于从栈中获取一个值:
int lua_toboolean(lua_State * L, int index);
lua_Number lua_tonumber(lua_State *L, int index)
lua_Integer lua_tointeger(lua_State * L, int index)
const char * lua_tolstring(lua_State * L, int index, size_t * len)
size_t lua_objlen(lua_State* L, int index)
5) API还提供了一些用于普通栈操作的函数:
int lua_gettop(lua_State * L):返回栈中元素的个数,也可以说是栈顶元素的索引。
void lua_settop(lua_State *L, int index) :将栈顶位置设为一个指定的位置,即修改栈中元素的数量。如果之前的栈顶比新设置的的更高,高出来的元素会被抛弃;反之,会向栈中压入nil来补足大小。lua_settop(L, 0)能清空栈。lua_pop(L, n)用于从栈中弹出n个元素
void lua_pushvalue(lua_State *L , int index):会将指定索引上值的副本压入栈
void lua_remove(lua_State *L, int index):删除指定索引上的元素,并将该位置之上的所有元素下移以填补空缺。
void lua_insert(lua_State *L, int index):会上移指定位置之上的所有元素以开辟一个槽的空间,然后将栈顶元素移到该位置。
vodi lua_replace(lua_State *L, int index):弹出栈顶的值,并将该值设置到指定索引上,但它不会移动任何东西 
第一个使用C API操作lua的demo,编译命令:  gcc first.c -llua
#include <stdio.h>
#include "lua.h"
#include "lauxlib.h"

static void stackDump(lua_State *L)
{
int i;
int top = lua_gettop(L);
for (i = 1; i<= top; i++) {
int t = lua_type(L, i);
switch(t) {
case LUA_TSTRING:{
printf("'%s'", lua_tostring(L, i));
break;
}
case LUA_TBOOLEAN: {
printf(lua_toboolean(L, i)?"true":"false");
break;
}
case LUA_TNUMBER: {
printf("%g", lua_tonumber(L, i));
break;
}
default: {
printf("%s", lua_typename(L, t));
break;
}
}

printf(" ");
}
printf("\n");
}

int main(void)
{
lua_State *L = luaL_newstate();

lua_pushboolean(L, 1);
lua_pushnumber(L, 10);
lua_pushnil(L);
lua_pushstring(L, "hello");

stackDump(L);

lua_pushvalue(L, -4); // 将指定索引上的副本压入栈,即再压入一个1 true 10 nil hello ture
stackDump(L);

lua_replace(L, 3); // 弹出栈顶的值,并将该值放到指定的索引上,即将栈顶的1放到nil的位置上 ture 10 true hello
stackDump(L);

lua_settop(L, 6); // 设置栈顶位置,即添加两个nil, true 10 true hello nil nil
stackDump(L);

lua_remove(L, -3); // 删除指定索引上的值,并将上面的值下移,即删除hello, true 10 true nil nil
stackDump(L);

lua_settop(L, -5); // true
stackDump(L);

lua_close(L);

return 0;
}
6) C API使用setjmp机制来进行异常处理;
如果发生了内存分配错误,又不想结束程序。一是,设置一个“紧急”函数,让它不要把控制权返回给lua。第二种,是让代码在“保护模式”下运行。大多数应用都采取第二种,调用lua_pcall来运行Lua代码。如果要保护那些与Lua交互的C代码,可以使用lua_cpcall,类似于lua_pcall,但是接受一个C函数作为参数,然后调用C函数,将一个函数压入栈中不会有内存分配失败的可能。
7)库代码中的错误处理
当为Lua编写库函数时,却只有一种标准的错误处理方法,当一个C函数检测到一个错误时,它就应该调用lua_error,lua_error函数会清理Lua中所有需要清理的东西,然后跳转到发起执行的那个lua_pcall,并附上一条错误信息
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: