Lua5.1编程四:Lua与C交互基础

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以压栈对应的数据。
[plain]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. void lua_pushnil(lua_State *L);  
  2. void lua_pushboolean(lua_State *L,int bool)  
  3. void lua_pushnumber(lua_State *L,lua_Number n)  
  4. void lua_pushinteger(lua_State *L,lua_Integer n)  
  5. void lua_pushstring(lua_State *L,const char* s);  
  6. void lua_pushlstring(lua_State *L,const char* s,size_t len);  

默认栈中有20个空糟,可以用来存放数据,需要有必要可以检查栈的空间是否满足需要
int lua_checkstack(lua_State* L,int sz)

栈中的数据底从1开始,但从顶部访问时,则用负的索引,-1表示顶部元素,-2表示其下一个元素,依次推

每种类型都有一个函数用来从栈中取出其值

[plain]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. lua_tostring(lua_State *L, int idx)  
  2. lua_tolstring(lua_State *L, int idx,size_t* len)  //从栈中获取字符串,长度在于len中。  
  3. lua_tonumber(lua_State *L, int idx)  
  4. lua_tointeger(lua_State *L, int idx)  
  5. lua_objlen(lua_State *L, int idx)      //可以获取表长度、userdata的大小  
每种类型都有相应的类型判断函数
[plain]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. lua_isnumber(lua_State *L, int idx)  
  2. lua_isstring(lua_State *L, int idx)  
  3. lua_istable(lua_State *L, int idx)  
有一个总的类型分析函数
[plain]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. int lua_type(lua_State *L, int idx) //返回类型,包含LUA_TNIL,TBOOLEAN,TNUMBER,TSTRING,TTABLE,TTHREAD,TUSERDATA,TFUNCTION。  
  2. char* lua_typename(lua_State*L,int type)    
栈的部分操作
[plain]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. int  lua_gettop(lua_State *L)             获取栈中元素个数  
  2. void lua_settop(lua_State *L, int idx)    重置栈顶指针指向idx的位置  
  3. void lua_pushvalue(lua_State *L, int idx) 复制idx位置元素并压入到栈顶  
  4. void lua_remove(lua_State *L, int idx)    移除指定idx位置的元素,其上元素依次下移  
  5. void lua_insert(lua_State *L, int idx)    把栈顶元素插入到idx位置  
  6. 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中执行。
[plain]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. lua_State *L = luaL_newstate();  
  2. luaL_openlibs(L);  
  3. if( luaL_loadbuffer(L,buff,strlen(buff),"line") ||  
  4.     lua_pcall(L,0,0,0))  
  5. {  
  6.     //handle error  
  7.     lua_pop(L,1);  
  8. }  
  9. lua_close(L);  

2.2 传递表结构数据的API
[plain]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. int  luaL_loadfile(lua_State*L,char* file,int mode)  在Lua中加载文件  
  2.   
  3. void lua_getglobal(lua_State* L,char* name)          将Lua全局环境中名为name的变量压入栈顶  
  4. void lua_setglobal(lua_State* L,char* name)          弹栈,把名为name的Lua全局变量的值设置为此值  
  5.   
  6. void lua_getfield(lua_State* L,int idx,char* key)    将idx位置的table的key对应的值压入栈顶  
  7. void lua_setfield(lua_State* L,int idx,char* key)    将idx位置的table的key对应的值设置为栈顶元素的值  
  8.   
  9. void lua_gettable(lua_State* L,int idx)  当前栈顶为key,把idx位置的table的key对应的值压入栈顶  
  10. void lua_settable(lua_State* L,int idx)  当前栈顶为value,栈顶下为key,把idx位置的table的key的值置为value  
  11.   
  12. void lua_rawget(lua_State* L,int idx) 类lua_gettable,效率高  
  13. void lua_rawset(lua_State* L,int idx) 类lua_settable,效率高  
  14.   
  15. 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函数从栈中获取传递进来的参数;函数的返回值表示结果的个数,结果需要在函数中压入到栈中。
[plain]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. static int wrap_sin(lua_State* L)  
  2. {  
  3.     double d = lua_tonumber(L,1);  
  4.     double r = sin(d);  
  5.     lua_pushnumber(L,r);  
  6.   
  7.     return 1;  
  8. }  

实现函数以后,还必须能让LUA环境启动后能找到新定义的C函数。对于简单的函数,可以在linit.c中的luaL_openlibs()中函数压入栈,并为之设置全局函数名。
[plain]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. lua_pushfunction(L,wrap_sin);  
  2. lua_setglobal(L,"wsin");  

3.2 使用C模块
只使用单个C函数基本上没有什么意义,最常用的是实现一个比较复杂的模块,然后让Lua支持之。其模式为:
[plain]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. //模块导出函数实现略,假定实现了func1,func2等一批导出函数  
  2. static const struct luaL_Reg exports[] = {  
  3.     {"fun1",func1},  
  4.     {"fun2",func2},  
  5.     ...  
  6.     {NULL,NULL}  
  7. }  
  8.   
  9. int luaopen_libname(lua_State* L)  
  10. {  
  11.     luaL_register(L,"libname",exports);  
  12.     return 1;  
  13. }  

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值