zoukankan      html  css  js  c++  java
  • lua与c之间交互详解(二)

    本篇主要讲解下c如何调用Lua的,即c作为宿主语言,Lua为附加语言。c和Lua之间是通过Lua堆栈交互的,基本流程是:把元素入栈——从栈中弹出元素——处理——把结果入栈。关于Lua堆栈介绍以及Lua如何调用c参考其他两篇。

    1. 加载运行Lua脚本

    通过luaL_newstate()创建一个状态机L,c与Lua之间交互的api的第一个参数几乎都是L,是因为可以创建多个状态机,调用api需指定在哪个状态机上操作。lua_close(L)关闭状态机。

    int main(int argc, char *argv[]){
        lua_State *L = luaL_newstate(); //创建一个状态机
        luaL_openlibs(L); //打开所有lua标准库
    
        int ret = luaL_loadfile(L, "c2lua.lua"); //加载但不运行lua脚本
        if(ret != LUA_OK){
            const char *err = lua_tostring(L, -1); //加载失败,会把错误信息压入栈顶
            printf("-------loadfile error = %s
    ", err);
            lua_close(L);
            return 0;
        }
    
        ret = lua_pcall(L, 0, 0, 0); //保护模式调用栈顶函数
        if(ret!=LUA_OK){
            const char *err = lua_tostring(L, -1); //发生错误,会把唯一值(错误信息)压入栈顶
            printf("-------pcall error = %s
    ", err);
            lua_close(L);
            return 0;
        }
    
        lua_close(L);
        return 0;
    }

     luaL_loadfile(L, filename):加载指定的Lua脚本,加载成功,把一个编译好的代码块作为Lua函数压入栈顶。若加载失败,会把错误信息压入栈顶

    lua_pcall(L,0,0,0),在保护模式下调用栈顶函数,稍后介绍调用Lua函数时说明几个参数的作用,若运行发生错误,会把唯一值(错误信息)压入栈顶。luaL_openlibs(L)打开所有标准库,若不调用,则会因为找不到print函数而报错。

    -- c2lua.lua
    print("------c2lua------")

     在c中加载运行Lua脚本的流程通常是,luaL_newstate、luaL_openlibs、luaL_loadfile、lua_pcall

     2. 操作Lua中全局变量

    lua_getglobal(L, name),获取Lua脚本中命名为name的全局变量并压栈,然后c通过栈获取

    void test_global(lua_State *L){ //读取,重置,设置全局变量
        lua_getglobal(L, "var"); //获取全局变量var的值并压入栈顶
        int var = lua_tonumber(L, -1);
        printf("var = %d
    ", var);
        lua_pushnumber(L, 10);
        lua_setglobal(L, "var"); //设置全局变量var为栈顶元素的值,即10
        lua_pushstring(L, "c str");
        lua_setglobal(L, "var2"); //设置全局变量var2为栈顶元素的值,即c str
    
        lua_getglobal(L, "f");
        lua_pcall(L,0,0,0);
    }
    //c2lua.lua
    var = 5
    
    function f()
        print("global var = ", var, var2)
    end

      lua_setglobal(L, name),弹出栈顶的值,并设置为全局变量name的新值(name可以在Lua中未定义)

    3. 调用Lua中函数

    通过lua_pcall这个api在保护模式下调用一个Lua函数

    int lua_pcall (lua_State *L, int nargs, int nresults, int msgh);

    nargs是函数参数的个数,nresults是函数返回值的个数。约定:调用前需要依次把函数,nargs个参数(从左向右)压栈(此时最后一个参数在栈顶位置),然后函数和所有参数都出栈,并调用指定的Lua函数。如果调用过程没有发生错误,会把nresults个结果(从左向右)依次压入栈中(此时最后一个结果在栈顶位置),并返回成功LUA_OK。如果发生错误,lua_pcall会捕获它,把唯一返回值(错误信息)压栈,然后返回特定的错误码。此时,如果设置msgh不为0,则会指定栈上索引msgh指向的位置为错误处理函数,然后以错误信息作为参数调用该错误处理函数,最后把返回值作为错误信息压栈。

    void test_function(lua_State *L){ //调用lua函数
        lua_getglobal(L, "f1");
        lua_pcall(L, 0, 0, 0); //调用f1
        lua_getglobal(L, "f2");
        lua_pushnumber(L, 100);
        lua_pushnumber(L, 10);
        lua_pcall(L, 2, 2, 0); //调用f2
        lua_getglobal(L, "f3");
        char *str = "c";
        lua_pushstring(L, str);
        lua_pcall(L,1,1,0); //调用f3
    }
    // c2lua.lua
    function f1()
        print("hello lua, I'm c!")
    end
    
    function f2(a, b)
        return a+b, a-b
    end
    
    function f3(str)
        return str .. "_lua"
    end

    打印出栈的数据如下:栈顶是f3的返回值,接着是f2的2个返回值

    4. 操作Lua中的table

    对表的操作主要有查找t[k]、赋值t[k]=v以及遍历表。

    //c2lua.lua
    t = {1, 2, ["a"] = 3, ["b"] = {["c"] = 'd'}}
    int lua_getfield (lua_State *L, int index, const char *k);

    查找,把t[k]的值压栈,t为栈上索引index指向的位置,跟Lua一样该api可能触发"index"事件对应的元方法,等价于lua_pushstring(L,const char*k)和lua_gettable(L, int index)两步,所以通常用lua_getfield在表中查找某个值。

    void lua_setfield (lua_State *L, int index, const char *k);

     赋值,等价于t[k]=v,将栈顶的值(v)出栈,其中t为栈上索引index指向的位置,跟Lua一样该api可能触发“newindex”事件对应的元方法。需先调用lua_pushxxx(L,v)将v入栈,再调用lua_setfield赋值。

    void test_table(lua_State *L){
        // 读取table
        lua_getglobal(L, "t");
        lua_getfield(L, 1, "a");  //从索引为1的位置(table)获取t.a,并压栈
        lua_getfield(L, 1, "b");
        lua_getfield(L, -1, "c"); //从索引为-1的位置(栈顶)获取t.c,并压栈
    
        // 修改table
        lua_settop(L, 1); //设置栈的位置为1,此时栈上只剩一个元素t
        lua_pushnumber(L, 10);
        lua_setfield(L, 1, "a"); //t.a=10
        lua_pushstring(L, "hello c");
        lua_setfield(L, 1, "e"); //t.e="hello c"
    
        dump_table(L, 1); //遍历table number-number 1-1
                          //          number-number 1-2
                          //          string-number a-3
                          //          string-string e-hello c
                          //          string-table b-table
    
        //新建一个table
        lua_settop(L, 0); //清空栈
        lua_newtable(L); //创建一个table
        lua_pushboolean(L, 0);
        lua_setfield(L, 1, "new_a");
        lua_pushboolean(L, 1);
        lua_setfield(L, 1, "new_b");
    
        dump_table(L, 1); //遍历table string-boolean new_a-false
                          //          string-boolean new_b-true
    }                                

    注:lua_settop(L, int index)设置栈顶为index,大于index位置的元素都被移除,特别当index为0,即清空栈;如果原来的栈小于index,多余的位置用nil填充。

    int lua_next (lua_State *L, int index);

    通过lua_next遍历表t,t为索引index指向的位置,从栈顶弹出key,将该key的下一个key-value对压栈,此时key位于栈上-2位置,value位于-1位置。如果表中没有更多元素,则返回0。

    void dump_table(lua_State *L, int index){
        if(lua_type(L, index)!=LUA_TTABLE)
            return;
    // 典型的遍历方法 lua_pushnil(L);
    //nil入栈,相当于从表的第一个位置遍历 while(lua_next(L, index)!=0){ //没有更多元素,lua_next返回0 //key-value入栈, key位于栈上-2处,value位于-1处 printf("%s-%s ", lua_typename(L,lua_type(L,-2)), lua_typename(L,lua_type(L,-1))); lua_pop(L,1); //弹出一个元素,即把value出栈,此时栈顶为key,供下一次遍历 } } 

     总之,c调用lua的流程通常是:c把需要的数据入栈——Lua从栈中取出数据——执行Lua脚本——Lua把结果入栈——c从栈中获取结果

  • 相关阅读:
    Haproxy基于ACL做访问控制
    K8s之Prometheus监控
    kubernetes之PV及PVC案例
    K8s常见示例
    K8s之Web服务
    Ansible 部署k8s
    K8s之网络通信
    创建资源对象实例
    kubeadm搭建K8s集群
    Go基础之函数递归实现汉诺塔
  • 原文地址:https://www.cnblogs.com/RainRill/p/8398280.html
Copyright © 2011-2022 走看看