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);
}
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,但参数可以不存在,不存在,返回默认值。