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);
}

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

你可能感兴趣的:(Lua扩展)