lua是一种嵌入式语言,可以链接到其他车型的库,lua库看拓展内容 使用了lua的程序可以注册其他语言的函数来向lua 添加功能
c api 是一组能是c代码与lua 交换的函数, 包括 读写lua全局变量, 调用lua函数 ,运行lua 代码 注册 c函数给lua调用
lua 与 c 通信的主要方法是一个无处不在的虚拟栈, 所有api都会操作这个栈上的值,所有交换都在c与lua的差异都在栈上解决
#include
#include
#include
#include
#include
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);// 修改栈顶索引也就是修改栈内元素数量 这样的修改会丢去多余 及补充nil
void 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作为编程语言还可以执行逻辑运算等,可以让配置更清晰方便
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);// 创建table
lua_pushnumber(L, value);
lua_settable(L, -2, index);// 创建table属性
...
lua_setglobal(L, tName);// 赋值到 lua全局变量会弹出table
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中
lua调用c也使用了一个c调用lua相同的栈,c函数从栈获取函数参数,并且将结果压入栈,以及结果数量,
栈不是全局的结构,每个函数都有自己的局部私有栈,lua调用c时第一个参数总是局部栈的索引1,
所有的注册到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");
// 定义函数
static int l_dir(lua_State* L){
//...
}
static const struct lua_Reg mylib[] =
{
//FUNC NAME func pointer
{"dir", l_dir},
{NULL, NULL},
}
// 注册module到lua
luaL_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
//example
int 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主要是绑定函,从栈获取参数并将结果压入栈。