加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

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

发布时间:2020-12-14 22:03:06 所属栏目:大数据 来源:网络整理
导读:1 CAPI简介 Lua与C可以有两种方式进行交互,一种是把LUA的功能作为库进行使用。另一种是在LUA中调用C库的功能,二者都可以通过CPAI的方式在LUA与C之间建立起桥梁。主要的数据结构是一个虚拟栈,大部分API均会操作栈上的值,进行数据交换。栈由Lua管理,垃圾
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表示其下一个元素,依次推

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

派生到我的代码片

    lua_tostring(lua_State?*L,?int?idx)??
  1. lua_tolstring(lua_State?*L,?int?idx,size_t*?len)??//从栈中获取字符串,长度在于len中。??
  2. lua_tonumber(lua_State?*L,?int?idx)??
  3. lua_tointeger(lua_State?*L,?int?idx)??
  4. 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。??
  1. char*?lua_typename(lua_State*L,int?type)????
栈的部分操作

派生到我的代码片

    int??lua_gettop(lua_State?*L)?????????????获取栈中元素个数??
  1. void?lua_settop(lua_State?*L,?int?idx)????重置栈顶指针指向idx的位置??
  2. void?lua_pushvalue(lua_State?*L,?int?idx)?复制idx位置元素并压入到栈顶??
  3. void?lua_remove(lua_State?*L,?int?idx)????移除指定idx位置的元素,其上元素依次下移??
  4. void?lua_insert(lua_State?*L,?int?idx)????把栈顶元素插入到idx位置??
  5. 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();??
  1. luaL_openlibs(L);??
  2. if(?luaL_loadbuffer(L,buff,strlen(buff),"line")?||??
  3. ????lua_pcall(L,0))??
  4. {??
  5. ????//handle?error??
  6. ????lua_pop(L,1);??
  7. }??
  8. lua_close(L);??

2.2 传递表结构数据的API

派生到我的代码片

    int??luaL_loadfile(lua_State*L,char*?file,int?mode)??在Lua中加载文件??
  1. ??
  2. void?lua_getglobal(lua_State*?L,char*?name)??????????将Lua全局环境中名为name的变量压入栈顶??
  3. void?lua_setglobal(lua_State*?L,char*?name)??????????弹栈,把名为name的Lua全局变量的值设置为此值??
  4. ??
  5. void?lua_getfield(lua_State*?L,int?idx,char*?key)????将idx位置的table的key对应的值压入栈顶??
  6. void?lua_setfield(lua_State*?L,char*?key)????将idx位置的table的key对应的值设置为栈顶元素的值??
  7. void?lua_gettable(lua_State*?L,int?idx)??当前栈顶为key,把idx位置的table的key对应的值压入栈顶??
  8. void?lua_settable(lua_State*?L,int?idx)??当前栈顶为value,栈顶下为key,把idx位置的table的key的值置为value??
  9. void?lua_rawget(lua_State*?L,int?idx)?类lua_gettable,效率高??
  10. void?lua_rawset(lua_State*?L,int?idx)?类lua_settable,效率高??
  11. 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)??
  1. {??
  2. ????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);??
  3. ????lua_pushnumber(L,r);??
  4. ????return?1;??
  5. }??

实现函数以后,还必须能让LUA环境启动后能找到新定义的C函数。对于简单的函数,可以在linit.c中的luaL_openlibs()中函数压入栈,并为之设置全局函数名。

派生到我的代码片

    lua_pushfunction(L,wrap_sin);??
  1. lua_setglobal(L,"wsin");??

3.2 使用C模块
只使用单个C函数基本上没有什么意义,最常用的是实现一个比较复杂的模块,然后让Lua支持之。其模式为:

派生到我的代码片

    //模块导出函数实现略,假定实现了func1,func2等一批导出函数??
  1. static?const?struct?luaL_Reg?exports[]?=?{??
  2. ????{"fun1",func1},??
  3. ????{"fun2",func2},??
  4. ????...??
  5. ????{NULL,NULL}??
  6. }??
  7. int?luaopen_libname(lua_State*?L)??
  8. ????luaL_register(L,"libname",exports);??
  9. ????return?1;??
  10. }??

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

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读