1 CAPI简介
Lua与C可以有两种方式进行交互,一种是把LUA的功能作为库进行使用。另一种是在LUA中调用C库的功能,二者都可以通过CPAI的方式在LUA与C之间建立起桥梁。主要的数据结构是一个虚拟栈,大部分API均会操作栈上的值,进行数据交换。栈由Lua管理,垃圾收集器能对无用数据进行回收。
CAPI的能力包括读写LUA全局变量、调用LUA函数、运行LUA代码,以及注册C函数以供LUA代码调用等。
通过lua.h头文件可以查看这些函数原型。
另一个头文件是lauxlib.h,其中声明函数原型为luaL_*,其使用CAPI,提供了高层抽象调用LUA的功能。
1.1 栈的相关操作
栈可以保存任何类型的Lua值,每种LUA类型都有一个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_pushstring(lua_State?*L,const?char*?s);??
- void?lua_pushlstring(lua_State?*L,const?char*?s,size_t?len);??
默认栈中有20个空糟,可以用来存放数据,需要有必要可以检查栈的空间是否满足需要
int lua_checkstack(lua_State* L,int sz)
栈中的数据底从1开始,但从顶部访问时,则用负的索引,-1表示顶部元素,-2表示其下一个元素,依次推
每种类型都有一个函数用来从栈中取出其值
lua_tostring(lua_State?*L,?int?idx)??
- lua_tolstring(lua_State?*L,?int?idx,size_t*?len)??//从栈中获取字符串,长度在于len中。??
- lua_tonumber(lua_State?*L,?int?idx)??
- lua_tointeger(lua_State?*L,?int?idx)??
- lua_objlen(lua_State?*L,?int?idx)??????//可以获取表长度、userdata的大小??
每种类型都有相应的类型判断函数
lua_isnumber(lua_State?*L,108); list-style:decimal-leading-zero outside; line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important"> lua_isstring(lua_State?*L,108); list-style:decimal-leading-zero outside; color:inherit; line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important"> lua_istable(lua_State?*L,?int?idx)??
有一个总的类型分析函数
int?lua_type(lua_State?*L,?int?idx)?//返回类型,包含LUA_TNIL,TBOOLEAN,TNUMBER,TSTRING,TTABLE,TTHREAD,TUSERDATA,TFUNCTION。??
- char*?lua_typename(lua_State*L,int?type)????
栈的部分操作
int??lua_gettop(lua_State?*L)?????????????获取栈中元素个数??
- void?lua_settop(lua_State?*L,?int?idx)????重置栈顶指针指向idx的位置??
- void?lua_pushvalue(lua_State?*L,?int?idx)?复制idx位置元素并压入到栈顶??
- void?lua_remove(lua_State?*L,?int?idx)????移除指定idx位置的元素,其上元素依次下移??
- void?lua_insert(lua_State?*L,?int?idx)????把栈顶元素插入到idx位置??
- void?lua_replace(lua_State?*L,?int?idx)???把栈顶元素复制到idx位置,并弹出栈顶元素??
1.2 CAPI的错误处理
由于C没有异常处理的相关机制,因此如果在LUA中出现异常,需要在C中进行处理。
一种方式是使用lua_atpanic一注册一个异常处理函数,以通知C程序发生了错误。
另一种方式是使用lua_pcall来来运行LUA代码。其会处理LUA中发生的异常并返回一个错误。p是protect的缩写,意为保护模式下的调用。
如果在LUA中调用C代码,则可以使用lua_cpcall,它将处理C中类似内存分配失败这类严重错误。在5.2中此函数已被删除,可以使用lua_pushfunction压入函数,然后调用lua_pcall来实现。
如果是在实现LUA的C模块代码,发生了错误,则需要调用lua_error,以返回给lua_pcall错误信息。
2 C中调用LUA
直接在C中调用LUA相当于实现一个嵌入式的LUA解析器。可以调用LUA的标准函数、或利用其标准库的功能。与LUA运行时交互的输入及输出数据,通过lua_State中的栈来实现。CAPI提供了众多接口以方便的在C中操作栈,以及向栈中放入C数据或LUA运行时数据的方法。
2.1 基础
下面的代码模拟了一个最简单的LUA解析器,从缓冲区中读取一个字符串并放到LUA中执行。
lua_State?*L?=?luaL_newstate();??
- luaL_openlibs(L);??
- if(?luaL_loadbuffer(L,buff,strlen(buff),"line")?||??
- ????lua_pcall(L,0))??
- {??
- ????//handle?error??
- ????lua_pop(L,1);??
- }??
- lua_close(L);??
2.2 传递表结构数据的API
int??luaL_loadfile(lua_State*L,char*?file,int?mode)??在Lua中加载文件??
- ??
- void?lua_getglobal(lua_State*?L,char*?name)??????????将Lua全局环境中名为name的变量压入栈顶??
- void?lua_setglobal(lua_State*?L,char*?name)??????????弹栈,把名为name的Lua全局变量的值设置为此值??
- ??
- void?lua_getfield(lua_State*?L,int?idx,char*?key)????将idx位置的table的key对应的值压入栈顶??
- void?lua_setfield(lua_State*?L,char*?key)????将idx位置的table的key对应的值设置为栈顶元素的值??
- void?lua_gettable(lua_State*?L,int?idx)??当前栈顶为key,把idx位置的table的key对应的值压入栈顶??
- void?lua_settable(lua_State*?L,int?idx)??当前栈顶为value,栈顶下为key,把idx位置的table的key的值置为value??
- void?lua_rawget(lua_State*?L,int?idx)?类lua_gettable,效率高??
- void?lua_rawset(lua_State*?L,int?idx)?类lua_settable,效率高??
- void?lua_newtable(lua_State*?L)?创建一个新表并压入栈顶??
2.3 调用Lua函数
把要调用的函数名压入栈顶,然后把参数依次压入栈顶,然后调用lua_pcall,最后从栈中弹出调用结果。
int lua_pcall(lua_State* L,int nargs,int nrets,int msgh);? 在Lua中执行L中的调用,参数分别是传入参数个数、返回参数个数。
如果是普通错误,则返回LUA_ERRRUN; 如果是内存错误则返回LUA_ERRMEM,如果是运行错误处理函数,则返回LUA_ERRERR。
3 LUA中调用C
3.1 调用C函数
LUA调用C函数也需要遵循一定的协议,C函数原原型必须是int func(lua_State* L);
传递参数也使用栈,C函数从栈中获取传递进来的参数;函数的返回值表示结果的个数,结果需要在函数中压入到栈中。
static?int?wrap_sin(lua_State*?L)??
- {??
- ????double?d?=?lua_tonumber(L,108); list-style:decimal-leading-zero outside; line-height:18px; margin:0px!important; padding:0px 3px 0px 10px!important"> ????double?r?=?sin(d);??
- ????lua_pushnumber(L,r);??
- ????return?1;??
- }??
实现函数以后,还必须能让LUA环境启动后能找到新定义的C函数。对于简单的函数,可以在linit.c中的luaL_openlibs()中函数压入栈,并为之设置全局函数名。
lua_pushfunction(L,wrap_sin);??
- lua_setglobal(L,"wsin");??
3.2 使用C模块
只使用单个C函数基本上没有什么意义,最常用的是实现一个比较复杂的模块,然后让Lua支持之。其模式为:
//模块导出函数实现略,假定实现了func1,func2等一批导出函数??
- static?const?struct?luaL_Reg?exports[]?=?{??
- ????{"fun1",func1},??
- ????{"fun2",func2},??
- ????...??
- ????{NULL,NULL}??
- }??
- int?luaopen_libname(lua_State*?L)??
- ????luaL_register(L,"libname",exports);??
- ????return?1;??
- }??
luaL_register将为模块生成一个同名的table,并用exports的信息填充这个表,然后把这个表放在栈L中。返回后表将带入到lua环境中。
lua源代码的lmathlib.c是一个很好的学习示例,它简单的封装了C的math库。
如果解释器支持动态链接,则上面这套机制可以正常工作。如果不支持动态链接,那么必须用新的模块重新编译Lua。
最简单的方法是在linit.c中,把luaopen_libname的调用加到luaL_openlibs函数中。
转自:http://blog.csdn.net/zzulp/article/details/23359885
(编辑:李大同)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|