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

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

发布时间:2020-12-14 22:11: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以压栈对应的数据。
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,int idx)
lua_isstring(lua_State *L,int idx)
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,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,1);
    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函数中。

(编辑:李大同)

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

    推荐文章
      热点阅读