Lua扩展

lua作为配置文件使用

-- win_conf.lua 定义窗口大小 width = 200 height = 300

使用LUA API分析这个文件,并获取width和height
void load(lua_State*L,const char*fname,int *w,int*h){ if(luaL_loadfile(L,fname) ||lua_pcall(L,0,0,0)) error(L,"cannot run config file:%s",lua_tostring(L,-1)); lua_getglobal(L,"width"); //push width lua_getglobal(L,"height"); //-1 push height if(!lua_isnumber(L,-2)) error(L,"width should be number"); if(!lua_isnumber(L,-1)) error(L,"height should be number"); *w = lua_tointeger(L,-2); *h = lua_tointeger(L,-1); }

【Lua扩展】table操作
lua 5.1提供了lua_getfiled和lua_setfield函数,lua5.1之前可以使用下面的方式,操作table
假设table在栈顶
-- win_conf.lua BLUE = {r=0,g=0,b=1} background = BLUE ... lua_getglobal(L,"background"); if(!lua_istable(L,-1)) error(L,"'background' is not a table") red = getfield(L,"r") ... /*假设table在栈顶*/ int getfield(lua_State*L,const char* key){ int result; lua_pushstring(L,key); //push key to stack lua_gettable(L,-2); // get background[key] 同时删除了KEY //lua 5.1提供了 lua_getfiled(L,-1,key),可以简化上面两行 if(!lua_isnumber(L,-1)) error(L,"..."); result = (int)lua_tonumber(L,-1)*255; lua_pop(L,1); /*删除数字*/ } /*table在栈顶*/ void setfield(lua_State* L,const char* key,int value){ lua_pushstring(L,key); lua_pushnumber(L,(double)value/255); lua_settable(L,-3) //table会自动变成栈顶 }

C调用Lua
-- f.lua function f(x,y) return x+y end -- f.c double f(double x,double y){ double z; lua_getglobal(L,"f"); //func in lua lua_pushnumber(L,x); //param 1 lua_pushnumber(L,y); //param 1 //调用(2个参数,1个结果) if(lua_pcall(L,2,1,0)){ error(L,"error running function 'f':%s",lua_tostring(L,-1)); //错误信息 } if(!lua_isnumber(L,-1)) error(L,"function 'f' must return a number"); z = lua_tonumber(L,-1); lua_pop(L,1); /*弹出返回值(弹出1个元素)*/ return z; }

Lua调用C
-- l_sin.c static in l_sin(lua_State* L){ //double d = lua_tonumber(L,1); double d = luaL_checknumber(L,1); //检查并转换(辅助库函数) lua_pushnumber(L,sin(d)); return 1; } //所有注册到Lua中函数原型都如下(lua.h中定义) typedef int (*lua_CFunction)(lua_State*); //这个函数无须再压入结果前清空栈。在它返回后,Lua会自动清空栈中结果之下的内容 //lua使用了l_sin之前必须先注册,使用lua_pushcfunction来进行注册 lua_pushcfuction(L,l_sin); lua_setglobal(L,"mysin");

C模块
当用C函数扩展Lua时,最好将代码设计为一个C模块。辅助库提供了一个函数luaL_register,这个函数接受一些C函数及名称,并将这些函数注册到一个与模块同名的table中去。
//声明一个数组(包含模块中所有的函数和名称) static const struct luaL_Reg mylib[] = { {"dir",l_dir}, {NULL,NULL} /*结尾*/ }; //注册函数 int luaopen_mylib(lua_State* L){ luaL_register(L,"mylib",mylib); return 1; } --lua中使用(自动加载mylib.so) require("mylib")

编写函数的技术:数组操作,字符串操作,状态保存
数组操作:针对数组型的table
//index为table在栈中的位置,key为数组索引 //等价于 lus_pushnumber(L,key); lua_rawget(L,index); void lua_rawgeti(lua_State* L,int index,int key); //等价于 lus_pushnumber(L,key); lua_insert(L,-2); lua_rawset(L,index); void lua_rawseti(lua_State* L,int index,int key);

字符串操作
//把字符串的子串([i,j])传入Lua lua_pushlstring(L,s+i,j-i+1) //l_split ("hi,ho,three")->{"hi","ho","there"} static int l_split(lua_State*L){ const char* s = luaL_checkstring(L,1); //第一个参数 const char* sep = luaL_checkstring(L,2); const char* e; int i = 1; lua_newtable(L); //函数结果 while((e=strchr(s,*sep))!=NULL){ lua_pushlstring(L,s,e-s); lua_rawseti(L,-2,i++); s=e+1; //跳过分隔符 } lua_pushlstring(L,s); //压入最后一个子串 lua_rawseti(L,-2,i); return 1; }

C函数中保存状态
注册表(registry)
注册表总是位于一个"伪索引"上,这个索引由LUA_REGISTRYINDEX定义,这个索引上有一个table。
注册表是一个普通的LUA table,可以用任何Lua值(除了nil)来索引它。Lua API中的大多数函数都接受伪索引,但是lua_remove,lua_insert这种操作栈本身的函数只能用普通索引。获得注册表中key为"KEY"的值
lua_getfield(L,LUA_REGISTRYINDEX,"KEY");

所有的C模块共享一个注册表,请注意KEY不要冲突。可以使用UUID做KEY,定义特定的宏来定义库。
另外注册表中不可以使用数字做key,这种key被“引用系统”所保留。这个系统是有辅助系统中的一系列函数组成,它可以向一个table存储value时,忽略如何创建一个唯一的key
int r = luaL_ref(L,LUA_REGISTR) //r 即为引用,当需要使用一个C变量保存一个指向Lua值得引用时,就需要使用引用系统 lua_rawgeti(L,LUA_REGESTRYINDEX,r); //将r关联的值压入栈 luaL_unref(L,LUA_REGSTRYINDEX,r); //释放引用和其值,再调用luaL_ref会返回相同的引用 //引用系统将nil视为一种特殊情况。为一个nil值调用luaL_ref是,不会创建新的应用,返回一个常量LUA_REFNIL lua_unref(L,LUA_REGSTRYINDEX,LUA_REFNIL); //无效果 lua_rawgeti(L,LUA_REGSTRYINDEX,LUA_REFNIL); //会压入nil

C 函数环境(lua5.1特性)
环境Table的伪索引是LUA_ENVIRONINDEX。尽可能的使用环境表来代替注册表,出发需要在不同模块间共享数据。
int luaopen_foo(lua_State*L){ lua_newtable(L); //创建环境table lua_replace(L,LUA_ENVIRONINDEX); //加入到环境表 luaL_register(L,"mylib",funcList); //自动设为mylib的环境 ... }

upvalue:闭包值存储,类似于C函数内静态变量机制.
每当在Lua中创建函数时,可以将任意数量的upvalue与这个函数关联。每个upvalue都可以保存一个Lua值。以后,调用这个函数时,可以同伪索引来访问这些upvalue。将这种C 函数与upvalue的关联称为closure.
static int counter(lua_State*L); //每次调用返回一个新的账号函数 int newCounter(lua_State*L){ //一个upvalue lua_pushinteger(L,0); //创建一个新的closure,1表示upvalue的数量 lua_pushcclosure(L,&counter,1); return 1; } //counter的定义 static int counter(lua_State*L){ int val = lua_tointeger(L,lua_upvalueindex(1)); lua_pushinteger(L,++va); lua_pushvalue(L,-1); //复制到栈顶 lua_replace(L,lua_upvalueindex(1)); //更新upvalue return 1; } //lua_upvalueindex(1)可以生成一个upvalue的伪索引,这个索引可以像其他栈索引一样使用

高级用法(元组的C实现)
-- lua x = tuple.new(10,"hi",{},3) print(x(1)) -->10 print(x(2)) -->hi print(x()) --> 10,hi table:0x...,3 -- lua_tuple.c int t_tuple(lua_State*L){ int op = luaL_opint(L,1,0); if(op == 0) { //无参数,显示所有的值 int i; for(i = 1; !lua_isnone(L,lua_upvalueindex(i)); i++) lua_pushvalue(L,lua_upvalueindex(i)); reutnr i-1; /*栈中的值*/ } else{ luaL_argcheck(L,0

由于可以不适应参数,luaL_optint来获取可选参数,此函数类似于luaL_checkint,但参数可以不存在,不存在,返回默认值。

    推荐阅读