Lua扩展

作者: 小城大麦 | 来源:发表于2016-08-16 17:30 被阅读192次

    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<op,1,"index out of range);//check index
            if(lua_isnone(L,lua_upvalueindex(op)))
                return 0;//无此字段
            lua_pushvalue(L,lua_upvalueindex(op));
            return 1;
        }
    }
    int t_new(lua_State* L){
        lua_pushcclosure(L,t_tuple,lua_gettop(L));//返回闭包对象
        return 1;
    }
    static const struct luaL_Reg tuplelib [] = {
        {"new',t_new},
        {NULL,NULL}
    };
    int luaopen_tuple(lua_State*L){
        luaL_register(L,"tuple",tuplelib);
    }
    

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

    相关文章

      网友评论

        本文标题:Lua扩展

        本文链接:https://www.haomeiwen.com/subject/upbpsttx.html