C与lua

来源:互联网 发布:efe矩阵ife矩阵哪个先 编辑:程序博客网 时间:2024/05/19 02:20

c api

lua是一种嵌入式语言,可以链接到其他车型的库,lua库看拓展内容 使用了lua的程序可以注册其他语言的函数来向lua 添加功能

c api 是一组能是c代码与lua 交换的函数, 包括 读写lua全局变量, 调用lua函数 ,运行lua 代码 注册 c函数给lua调用

lua 与 c 通信的主要方法是一个无处不在的虚拟栈, 所有api都会操作这个栈上的值,所有交换都在c与lua的差异都在栈上解决

c 中调用lua

#include<stdio.h>#include<string.h>#include<lua.h>#include<luaxlib.h>#include<lualib.h>int main(void){    char buff[256];    int error;    lua_State* L = luaL_newstate() /*打开lua*/    luaL_openlibs(L); /* 打开标准库 */     while(fgets(buff, sizeof(buff), stdin) != NULL)    {        /* pcall 调用lua 函数*/        error = luaL_Loadbuffer(L, buff, strlen(buff), "line") || lua_pcall(L, 0, 0, 0);        if(error)        {            fprintf(stderr, "%s", lua_tostring(L, -1));            lua_pop(L, 1);  /*从栈上获取错误信息*/        }    }    lua_close(L);    return 0;}

lua.h
提供创建lua环境调用lua函数的函数 读写lua 环境中的全局变量以及注册c函数给lua调用,以lua_为前缀

luaxlib.h 定义了辅助库,以luaL_ 为开头, lua 库没有全局变量 都在一个 lua_State 之中 所有的api都要传入该结构体

luaL_newState() 创建了一个新的lua 环境,其中没有任何函数(print都没有)需要 luaL_openlib() 来打开标准库

创建好state 之后 可以使用luaL_loadbuffer 来编译输入内容,并想栈压入程序块,任何调用lua_pcall
如果发生错误会向栈压入错误信息,可以使用lua_string()来获取 并打印,最后调用lua_pop 删除信息


lua 与c++直接数据交换需要考虑类型,内存管理的区别
lua使用了一个抽象的来保存数据,栈上保存任何类型的lua的值

———————->

先进先出

压入栈

c类型都有一个压栈函数

// c 向lua 栈添加元素void lua_pushnil(lua_State* L);void lua_pushboolean(lua_State* L, int bool);void lua_pushnumber(lua_State* L, lua_Number n);//double 类型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);//以0结尾的字符串

栈至少会有20 个空槽
lua_checkstack()检查栈是否有足够的空间

查询元素

lua api 使用整数索引来引用栈元素,第一个压入的元素索引是1 第二个为2 知道栈顶 也可以以栈顶为开始以负数为为索引来查询元素
-1 表示栈顶, -2表示下面的一个

调用lua_tostring(L, -1)会将栈顶元素作为字符串返回

使用lua_is*(lua_State* L, int index) 来检测类型
*代表number string table等

isnumber 和isstring 会检测lua变量能否转化为对应类型 使用对于数字 isstring 为真

以下函数来获取栈内元素

LUA_API lua_Number      (lua_tonumber) (lua_State *L, int idx);LUA_API lua_Integer     (lua_tointeger) (lua_State *L, int idx);LUA_API int             (lua_toboolean) (lua_State *L, int idx);LUA_API const char     *(lua_tolstring) (lua_State *L, int idx, size_t *len);LUA_API size_t          (lua_objlen) (lua_State *L, int idx);LUA_API lua_CFunction   (lua_tocfunction) (lua_State *L, int idx);LUA_API void           *(lua_touserdata) (lua_State *L, int idx);LUA_API lua_State      *(lua_tothread) (lua_State *L, int idx);LUA_API const void     *(lua_topointer) (lua_State *L, int idx);

其余栈操作

int lua_gettop(lua_State* L); // 返回栈中元素个数 注意是个数也就是栈顶的索引 具体值使用上面函数获取void lua_settop(lua_State* L, int index);// 修改栈顶索引也就是修改栈内元素数量 这样的修改会丢去多余 及补充nilvoid lua_pushvalue(lua_State* L, int index);// 索引上的值的副本压入栈,void lua_remove(lua_State* L, int index);//删除索引并移动元素填空,void lua_insert(lua_State* L, int index);//将栈顶元素插入到该位置并移动元素void lua_replace(lua_State* L, int index);// 将栈顶的值弹出并插入到指定位置

拓展应用程序

下面介绍程序中实际使用lua的情况

作为配置

luaL_loadfile(Lua_State* L, const char* fname); // 加载lua文件lua_pcall (lua_State *L, int nargs, int nresults, int errfunc);// 执行lua 如果发生错误会把一个错误消息压入栈给c api获取处理lua_getglobal(L, fieldName)// 获取lua全局变量压入栈 会将lua的全局变量压入栈并给c pi调用

加载并且读取lua 程序使宿主程序可以不修改而达到目的, 同时lua作为编程语言还可以执行逻辑运算等,可以让配置更清晰方便

table操作

lua table 操作函数只有一个lua_gettable() 需要知道table的位置,以及key,就能压入value 弹出key

background = { r = 255, g = 144, b = 122 }
以下函数假定目标table 在栈顶(-1位置)//对于5.1之后的lua 获取table的操作可以简化为//获取table属性值lua_getfield(L, -1, key); //字符串没有压入栈所以 -1 是table的索引//等价于lua_pushstring(L, key); lua_gettable(L, -2);/*****************/// 修改table属性值lua_pushnumber(L, value);lua_setfield(L, -2, index);// 会弹出value// 等价于lua_pushstring(L, index);lua_pushnumber(L, value);lua_settable(L, -3);// 对于不同类型只需要使用对应的函数替代即可

创建一个lua table

lua_newtable(L);// 创建tablelua_pushnumber(L, value); lua_settable(L, -2, index);// 创建table属性...lua_setglobal(L, tName);// 赋值到 lua全局变量会弹出table

调用lua函数

c调用lua函数只需要将函数载入到栈中并压入参数即可完成调用

lua_getglobal(L, "f"); // 全局变量中获取函数变量并且压入栈lua_pushnumber(L, x); // 压入一个参数 lua_pushnumber(L, y); // 压入第二个参数// 调用lua函数, 2个参数 || 1个返回值 || 错误处理函数索引 栈中的函数索引 0代表没有if(lua_pcall(L, 2, 1, 0) != 0) // 此调用会弹出函数及参数    error(L, "error running function %s", lua_tostring(L, -1));// 查看函数调用结果if(!lua_isnumber(L, -1))    error(L, "function return error type")int z = lua_tonumber(L, -1);lua_pop(L, 1); // 弹出返回值return z;

从lua到c

使用lua来拓展程序,还可以将c的函数注册到lua中

lua调用c也使用了一个c调用lua相同的栈,c函数从栈获取函数参数,并且将结果压入栈,以及结果数量,

栈不是全局的结构,每个函数都有自己的局部私有栈,lua调用c时第一个参数总是局部栈的索引1,

c函数

所有的注册到lua的c函数都有相同的原型,

static int l_sin(lua_State* L){    double d = luaL_checknumber(L, 1); // get arg with type check    lua_pushnumber(L, sin(d)); // push return     return 1;}typedef int (*lua_CFunction) (lua_State* L);  // 返回值表示压入栈的返回值数量, 函数返回后会自动删除栈中结果// 注册函数lua_pushcfunction(L, l_sin);// 添加为全局变量lua_setglobal(L, "mysin");

c 模块 (注册到lua module)

// 定义函数static int l_dir(lua_State* L){    //...}static const struct lua_Reg mylib[] = {    //FUNC NAME func pointer     {"dir", l_dir},    {NULL, NULL},}// 注册module到lualuaL_register(L, "mylib", myLib);

最后将c代码编译成动态链接库加入path之中即可使用
在lua中只要使用require就可以获取对应module的table

require “mylib”

数组操作

数组是编程中使用最广泛的数据结构,为了方便与性能,lua为数组提供了专用函数,在lua 数组就是table

//                      table 栈上位置  table的索引void lua_rawgeti(lua_State* L, int index, int key);//<==>lua_pushnumber(L, key); // 压入table索引值lua_rawget(L, t);// 从栈上的位置找到table 并且以栈顶的索引访问table并且返回值void lua_rawseti(lua_State* L, int index, int key);//<==>lua_pushnumber(L, key);// 压入table索引值lua_insert(L, -2);// 把key插入到前值之后,lua_rawset(L, t)// 插入table//exampleint l_map(lua_State* L){    int i, n;    lua_checktype(L, 1, LUA_TTABLE);    lua_checktype(L, 2, LUA_TFUNCTION);    n = lua_objlen(L, 1);// get table size    for(i = 1, i <= n; i++)    {        lua_pushvalue(L, 2);//压入function         lua_rawgeti(L, 1, i);// 获取到table的成员t[i]并压入栈        lua_call(L, 1, 1);//调用 function(t[i])        lua_rawseti(L, 1, i);// t[i] = call result    }    return 0;// 函数不向lua返回值}

总结

c 与lua的交互都在一个栈上,c调用lua根据栈上值的信息完成读取lua数据,调用lua函数等操作,lua调用c主要是绑定函,从栈获取参数并将结果压入栈。

原创粉丝点击