zoukankan      html  css  js  c++  java
  • Lua程序设计(4th) 第四部分 C语言API

    第27章 C语言API总览

        lua.h(前缀 lua_): 声明了 Lua 提供的基础函数,其中包括创建新 Lua 环境的函数、调用 Lua 函数的函数等等。

        lauxlib.h(前缀 luaL_): 辅助( auxiliary library) 使用 lua.h 提供的基础 API 来提供更高层次的抽象,不能访问 Lua 的内部元素,而只能通过lua.h 中声明的官方基础 API 完成所有工作。
    #include <stdio.h>
    #include <string.h>
    #include "lua.h"
    #include "lauxlib.h"
    #include "lualib.h"
    int main (void) {
        lua_State *L = luaL_newstate();   //打开 Lua
        luaL_openlibs(L);                 //打开所有的标准库
        char buff[256];
        while (fgets(buff, sizeof(buff), stdin) != NULL) {
            //1. 编译用户输入的每一行内容,并向栈中压入编译得到的函数
            //2. 从栈中弹出编译后得到的函数,并以保护模式运行
            int error = luaL_loadstring(L, buff) ||
                        lua_pcall(L, 0, 0, 0);
            //3. 错误信息处理:获取栈中的错误信息并打印,随后删除栈中的错误信息
            if (error) { 
                fprintf(stderr, "%s
    ", lua_tostring(L, -1));
                lua_pop(L, 1);
            }
        }
        lua_close(L);
        return 0;
    }
    //C API压栈函数:
    void lua_pushnil      (lua_State *L);
    void lua_pushboolean  (lua_State *L, int bool);
    void lua_pushnumber   (lua_State *L, lua_Number n);  //默认double
    void lua_pushinteger  (lua_State *L, lua_Integer n); //默认long long
    void lua_pushlstring  (lua_State *L, const char *s, size_t len);//任意二进制数据,Lua会生成一个内部副本或复用已有字符串
    void lua_pushstring   (lua_State *L, const char *s); //''结尾的字符串,Lua会生成一个内部副本或复用已有字符串
    
    // minimum Lua stack available to a C function
    // #define LUA_MINSTACK 20
    // 检查找中是否有足够的空间
    int lua_checkstack(lua_State *L, int sz);
    void lual_checkstack(lua_State *L, int sz, const char* msg);
    //C API 查询元素
    //栈顶  [3]     [-1]
    //     [2]     [-2]
    //栈底  [1]     [-3]
    
    //将栈顶的值返回
    int lua_toboolean (lua_State *L, int idx);
    const char *lua_tolstring (lua_State *L, int idx, size_t *len);
    int lua_tothread (lua_State *L, int idx);
    int lua_tonumber (lua_State *L, int idx);
    int lua_tointeger (lua_State *L, int idx);
    
    //检查找中的一个元素是否为特定的类型. 
    //函数 lua_isnumber 则是检查该值是否能被转换为此特定类型
    int lua_is*(lua_State *L, int idx); //*: nil, number, string, table
    lua_pushvalue(L, -4); // push the value of the index to the stack
    lua_replace(L, 3);    // pop a value and replace the index's
    lua_settop(L, 6);     // set the top index, fill 'nil'
    lua_rotate (L, idx, -1);//将idx处元素移动到-1位置
    #define lua_remove(L,idx) (lua_rotate(L, (idx), -1), lua_pop(L, 1))
    lua_insert(L, -2);    // move top element to the bottom
    LUA_POP(L, 1); 

    第28章 扩展应用

        读取lua配置文件

    --color.lua
    BLUE = {r=0.1, g=0.1, b=1}
    RED = {r=1, g=0.1, b=0}
    background = BLUE
    background = RED
    /* assume that table is on the stack top */
    int getfield(lua_State *L, const char *key)
    {
        int result;
        lua_pushstring(L, key);
        lua_gettable(L, -2); /* push background[key] and remove key*/
    
        if (!lua_isnumber(L, -1))
            error(L, "invalid component in background color");
        result = lua_tonumber(L, -1) * MAX_COLOR;
        lua_pop(L, 1); /* remove number */
        return result;
    }
    
    void loadTable()
    {
        char filename[] = "c_call_lua/color.lua";
        lua_State *L = luaL_newstate();
        luaL_openlibs(L);      // This is necessary for os.genenv
        if (luaL_loadfile(L, filename) || lua_pcall(L, 0, 0, 0)) {
            error(L, "cannot run configuration file: %s", lua_tostring(L, -1));
        }
    
        lua_getglobal(L, "background");
        if (!lua_istable(L, -1))
            error(L, "`background' is not a valid color table");
        int red = getfield(L, "r");
        int green = getfield(L, "g");
        int blue = getfield(L, "b");
        printf ("r = %d, g = %d, b = %d
    ", red, green, blue);
        lua_close(L);
    }
    void error (lua_State *L, const char *fmt, ...) {
        va_list vl;
        va_start(vl, fmt);
        vfprintf(stderr, fmt, vl);
        va_end(vl);
        lua_close(L);
        exit(EXIT_FAILURE);
        printf ("won't print
    ");
    }
    
    void callLuaf(lua_State *L, const char *func, const char *sig, ...) {
        lua_getglobal(L, func);
        //if (lua_getglobal(L, func) != 0)
        //    error(L, "error getting function `%s': %s", func, lua_tostring(L, -1));
        int narg = 0;/* number of arguments */
        va_list vl;
        va_start(vl, sig);
        while (*sig) {
            /* push arguments */
            switch (*sig++) {
            case 'd': {/* double argument */
                double d = va_arg(vl, double);
                lua_pushnumber(L, d);
                break;
            }
            case 'i': {/* int argument */
                int i = va_arg(vl, int);
                lua_pushnumber(L, i);
                break;
            }
            case 's': {/* string argument */
                char* c = va_arg(vl, char*);
                lua_pushstring(L, c);
                break;
            }
            case '>':
                goto endwhile;
            default:
                error(L, "invalid option (%c)", *(sig - 1));
            }
            narg++;
            luaL_checkstack(L, 1, "too many arguments");
        }
    
    endwhile:
        /* do the call */
        int nres = strlen(sig); /* number of results */
        /* number of expected results */
        if (lua_pcall(L, narg, nres, 0) != 0) /* do the call */
            error(L, "error running function `%s': %s", func, lua_tostring(L, -1));
        /* retrieve results */
        nres = -nres; /* stack index of first result */
        while (*sig) { /* get results */
            switch (*sig++) {
            case 'd': /* double result */
                if (!lua_isnumber(L, nres)) error(L, "wrong result type");
                *va_arg(vl, double *) = lua_tonumber(L, nres);
                break;
            case 'i': /* int result */
                if (!lua_isnumber(L, nres)) error(L, "wrong result type");
                *va_arg(vl, int *) = (int)lua_tonumber(L, nres);
                break;
            case 's': /* string result */
                if (!lua_isstring(L, nres)) error(L, "wrong result type");
                *va_arg(vl, const char **) = lua_tostring(L, nres);
                break;
            default:
                error(L, "invalid option (%c)", *(sig - 1));
            }
            nres++;
        }
        va_end(vl);
    }
    
    
    int callwk(const char *filename, const char* func) {
        lua_State *L = luaL_newstate();
        luaopen_base(L);
        luaopen_table(L);
        luaopen_package(L);
        luaopen_io(L);
        luaopen_string(L);
        luaopen_math(L);
        luaL_openlibs(L); // open all the libs above
        if (luaL_dofile(L, filename))
            error(L, "luaL_dofile: %s
    ", lua_tostring(L, -1));
        callLuaf(L, func, "");
        lua_close(L);
    
        //double x = 3.0;
        //double y = 2.0;
        //double z = 0;
        //callLuaf(L, func, "dd>d", x, y, &z);//call func(L, x, y);
        //printf ("In %s, %s(%f, %f) = %f
    ", filename, func, x, y, z);
        return 0;
    } 

    第29章 在Lua中调用C语言

    package.cpath = '/home/yoyu/hub/testcode/lua/api/?.so;'    --搜索so模块
    local lib = require "libtest"
    print("call c lib: lib.lsum(23,17) = "..lib.lsum(23,17))
    local files = lib.ldir(".") -- Get file list in current dir 
    //libtest.c
    int sum(lua_State *L){
        double d1 = luaL_checknumber(L, 1);
        double d2 = luaL_checknumber(L, 2);
        lua_pushnumber(L, d1+d2);
        return 1;
    }
    
    int dir (lua_State *L) {
        DIR *dir;
        struct dirent *entry;
        int i;
        const char *path = luaL_checkstring(L, 1); 
        /* open directory */
        dir = opendir(path);
        if (dir == NULL) {
            lua_pushnil(L);
            /* error opening the directory? */
            /* return nil and ... */
            lua_pushstring(L, strerror(errno)); /* error message */
            return 2; /* number of results */
        }   
        /* create result table */
        lua_newtable(L);
        i = 1;
        while ((entry = readdir(dir)) != NULL) {
            lua_pushnumber(L, i++); /* push key */
            lua_pushstring(L, entry->d_name); /* push value */
        //printf("entry->d_name : %s
    ", entry->d_name);
            lua_settable(L, -3);
        }   
        closedir(dir);
        return 1;
        /* table is already on top */
    }
    
    //Name of array mylib can be changed.
    static const struct luaL_Reg mylib[] = {
        {"lsum", sum},
        {"ldir", dir},
        {NULL, NULL}
    };
    
    //Function name can not be changed.
    int luaopen_libtest(lua_State *L){ 
        luaL_newlib(L, mylib);
        return 1;
    }

      

    #Makefile
    CFLAG1=-g -Wall -llua -shared -fPIC           #build lib
    CFLAG2=-g -Wall -llua -lm -ldl -ltest -fPIC   #use lib in c

        Lua 语言中的每个协程都有自己的软栈( soft stack ), 对于 C 函数的调用 ,解释器必须使用 C 语言栈。因此, Lua 中的协程不能挂起 C 函数的执行:如果一个 C 函数位于从 resume 到对应yield 的调用路径中,那么 Lua 无法保存 C 函数的状态以便于在下次 resume 时恢复状态。因此,Lua 5.1 不能将其挂起。

    在 Lua 5.2 及后续版本中,用延续(continuation) 改善了对这个问题的处理。
    --test.lua:pcall 是一个 C 语言函数
    co = coroutine.wrap(function() print(pcall(coroutine.yield)) end)
    co()
    --~/hub/lua/lua-5.1.4/src/lua testlua/test.lua ---> false attempt to yield across metamethod/C-call boundary
    --~/hub/lua/lua-5.3.6/src/lua testlua/test.lua ---> OK

     第30章 编写C函数的技巧

        当 C 函数接收到一个 Lua 字符串为参数时,必须遵守两条规则: 在使用字符串期 间不能从栈中将其弹出,而且不应该修改字符串 。当 C 函数需要创建一个返回给 Lua 的字符串时,要求则更高。需要 C 语言代码负责缓冲区的分配/释放、缓冲区溢出。因此,Lua API提供了一些函数来帮助完成这些任务。
    //test.lua
    local s = "hi,,there"
    print("will split:"..s)
    local split_result = lib.lsplit(s, "i")
    printAll(split_result, "split_result", 2, "hh ")
    //libtest.c
    int 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); /* result */
        /* repeat for each separator */
        while ((e = strchr(s, *sep)) != NULL) {
            lua_pushlstring(L, s, e - s); /* push substring */
            lua_rawseti(L, -2, i++);      /* insert to table */
            s = e + 1;                    /* skip separator */
        }   
        /* push last substring */
        lua_pushstring(L, s); 
        lua_rawseti(L, -2, i); 
        return 1; /* return the table */
    }

         lua_concat(L, n) 会连接(并弹出)栈最顶端的 n 个值,并将结果压人栈。

        const char *lua_pushfstring (lua_State *L, const char* fmt, ...); 类似于 C 函数sprintf。然而不同的是, lua_pushfstring 会动态地为我们创建缓冲区 ,将结果字符串压入栈中并返回一个指向它的指针。
        当只需连接几个字符串时, lua_concat 和 lua_pushfstring 都很有用。 不过,如果需要连接很多字符串(或字符),那么逐个连接就会非常低效。 此时,我们可以使用由辅助库提供的缓冲机制(buffer facility )。
        如果已知返回结果大小的上限值,使用下面的函数,参考lstrlib.c:str_upper。
    LUALIB_API char *luaL_buffinitsize (lua_State *L, luaL_Buffer *B, size_t sz);

        如果不知道返回结果大小的上限值,使用下面的函数:

    LUALIB_API void luaL_buffinit (lua_State *L, luaL_Buffer *B);
    LUALIB_API void luaL_addvalue (luaL_Buffer *B);
    LUALIB_API void luaL_addlstring (luaL_Buffer *B, const char *s, size_t l);
    LUALIB_API void luaL_addstring (luaL_Buffer *B, const char *s);
    #define luaL_addchar(B,c) 
      ((void)((B)->p < ((B)->buffer+LUAL_BUFFERSIZE) || luaL_prepbuffer(B)), (*(B)->p++ = (char)(c)))
    LUALIB_API void luaL_pushresult (luaL_Buffer *B);

        示例:函数 table.concat 一个简化的实现

    static int tconcat(lua_State *L)
    {
        luaL_Buffer b;
        int i, n;
        luaL_checktype(L, 1, LUA_TTABLE);
        n = luaL_len(L, 1);
        luaL_buffinit(L, &b);
        for (i = 1; i <= n; i++) {
            lua_geti(L, 1, i);      // Get string from table
            luaL_addvalue(&b);      // Put string in buff
        }
        luaL_pushresult(&b);
        char* s = lua_tostring(L, -1);
        printf("in c   : s = %s
    ", s);
        //lua_remove(L, -1);
        return 1;
    }

     注册表是一张只能被 C 代码访问的全局表 。 通常情况下,我们使用注册表来存储多个模块间共享的数据 。

    static int set_regref(lua_State *L)
    {
        int key1 = luaL_ref(L, LUA_REGISTRYINDEX);
        char data_of_key1[] = "char data of key1";
        lua_pushlightuserdata(L, (void *)&key1);  /* push address */
        lua_pushstring(L,  data_of_key1);         /* push value */
        lua_settable(L, LUA_REGISTRYINDEX);       /* registry[&key1] =  data_of_key1*/
        printf("key1 = %d set registry[&%p] = %s
    ", key1, &key1, data_of_key1);
    
        int key2 = luaL_ref(L, LUA_REGISTRYINDEX);
        int number_of_key2 = key2 * 1000;
        lua_pushlightuserdata(L, (void *)&key2);  /* push address */
        lua_pushnumber(L, number_of_key2);        /* push value */
        lua_settable(L, LUA_REGISTRYINDEX);       /* registry[&key2] = number_of_key2 */
        printf("key2 = %d set registry[&%p] = %d
    ", key2, &key2, number_of_key2);
    
        lua_rawgeti(L, LUA_REGISTRYINDEX, key1);  /* why needed? */
        lua_rawgeti(L, LUA_REGISTRYINDEX, key2);  /* why needed? */
        lua_pushnumber(L, (long)(&key1));        /* push address */
        lua_pushnumber(L, (long)(&key2));        /* push address */
        printf("key1 = %p
    ", (void *)lua_tointeger(L, -2));
        printf("key2 = %p
    ", (void *)lua_tointeger(L, -1));
    
        return 2; /* return the keys */
    }
    int get_regref(lua_State *L)
    {
        long key1_addr = luaL_checknumber(L, 1);
        long key2_addr = luaL_checknumber(L, 2);
        /* retrieve a string*/
        lua_pushlightuserdata(L, (void*)key1_addr); /* push address. */
        lua_gettable(L, LUA_REGISTRYINDEX);        /* retrieve value */
        const char *key1_data = lua_tostring(L, -1); /* convert to string*/
        printf("get registry[&%p] = %s
    ", (void*)key1_addr, key1_data);
        /* retrieve a number */
        lua_pushlightuserdata(L, (void*)key2_addr); /* push address */
        lua_gettable(L, LUA_REGISTRYINDEX);       /* retrieve value */
        int key2_data = lua_tonumber(L, -1);        /* convert to number */
        printf("get registry[&%p] = %d
    ", (void*)key2_addr, key2_data);
    
        luaL_unref(L, LUA_REGISTRYINDEX, (int)key1_addr);
        luaL_unref(L, LUA_REGISTRYINDEX, (int)key2_addr);
        printf("return
    ");
        return 0; /* return the table */
    }

      

    function test_reg()
        local key1, key2 = lib.lset_regref()
        print(string.format("%x", key1))
        print(string.format("%x", key2))
        lib.lget_regref(key1, key2)
    end

         上值( upvalue ) 实现了 一种类似于 C 语言静态变量 (只在特定的函数中可见)的机制。C 函数与其上值 的关联称为闭包 ( closure )。

    static int counter(lua_State *L)
    {
        int val = lua_tointeger(L, lua_upvalueindex(1));
        lua_pushinteger(L, ++val); //新值
        lua_copy(L, -1, lua_upvalueindex(1)); //用新值更新upvalue
        return 1; //返回新值
    }
    
    int newCounter(lua_State *L)
    {
        lua_pushinteger(L, 1); //压入upvalue,初始值为1
        lua_pushcclosure(L, &counter, 1); //创建闭包
        return 1; //返回闭包
    }
    function test_counter()
        local c1 = lib.lnewCounter()
        local c2 = lib.lnewCounter()
        print(c1(), c2(), c1(), c2(), c1(), c2())
    end

        元组是一种具有匿名字段的常量结构,我们可以用一个数值索引来获取某个特定的字段,或者一次性地获取所

    有字段 。 在我们的实现中,将元组表示为函数,元组的值存储在函数 的上值中 。

    int t_tupple(lua_State *L)
    {
        lua_Integer op = luaL_optinteger(L, 1, 0); //获取1个参数值,默认0
        if (op == 0) { //没有参数
            int i;
             /*将每一个有效的upvalue压栈*/
            for (i = 1; !lua_isnone(L, lua_upvalueindex(i)); ++i) {
                lua_pushvalue(L, lua_upvalueindex(i));
            }
            return i - 1; //值的个数
        } else {
            luaL_argcheck(L, 0 < op && op <= 256, 1, "index out of range");
            if (lua_isnone(L, lua_upvalueindex(op))) {
                return 0; //字段不存在
            }
            lua_pushvalue(L, lua_upvalueindex(op));
            return 1;
        }
    }
    
    int t_new(lua_State *L)
    {
        int top = lua_gettop(L);
        luaL_argcheck(L, top < 256, top, "too many fields");
        lua_pushcclosure(L, t_tupple, top);
        return 1;
    }
    function tupple_test()
        x = lib.lnew(10, "hi tupple", {}, 3)
        print(x(1))
        print(x(2))
        print(x())
        print(x(256))
    end

     第31章 C语言中的用户自定义类型

        用户数据(userdata),元表,数组访问

    #include "libtest.h"
    
    int newarray (lua_State *L) {
        int n = luaL_checkinteger(L, 1);
        size_t nbytes = sizeof(NumArray) + (n - 1)*sizeof(double);
        //把这块分配的内存压入栈,并把地址存入 a。
        NumArray *a = (NumArray *)lua_newuserdata(L, nbytes);
        a->size = n;
        luaL_getmetatable(L, "LuaBook.array");
        lua_setmetatable(L, -2);
        return 1; // new userdatum is already on the stack
    }
    
    //判断第一个参数的元表是否为 registry["LuaBook.array"]
    #define checkarray(L) (NumArray *)luaL_checkudata(L, 1, "LuaBook.array")
    
    static int getindex (lua_State *L) {
        NumArray *a = checkarray(L);
        int index = luaL_checkinteger(L, 2) - 1;
        luaL_argcheck(L, 0 <= index && index < a->size, 2,"index out of range");
        return index; /* return element address */
    }
    
    int getsize (lua_State *L) {
       NumArray *a = checkarray(L);
       lua_pushnumber(L, a->size);
       return 1;
    }
    
    static double *getelem (lua_State *L) {
        NumArray *a = checkarray(L);
        int index = getindex(L);
        return &a->values[index];    // return element address.
    }
    
    int setarray (lua_State *L) {
        double newvalue = luaL_checknumber(L, 3);
        *getelem(L) = newvalue;
        return 0;
    }
    
    int getarray (lua_State *L) {
        double ret = *getelem(L);
        lua_pushnumber(L, ret);
        return 1;
    }
    
    int array2string (lua_State *L) {
        NumArray *a = checkarray(L);
        lua_pushfstring(L, "array(%d)", a->size);
        return 1;
    }
    
    //Name of array mylib can be changed.
    static const struct luaL_Reg thelib[] = {
        {"new", newarray},
        {"set", setarray},
        {"get", getarray},
        {"size", getsize},
        {NULL, NULL}
    };
    
    static const struct luaL_Reg arraylib_m[] =
    {
        {"__newindex", setarray},
        {"__index", getarray},
        {"__len", getsize},
        {"__tostring", array2string},
        {NULL,NULL}
    };
    
    //Function name can not be changed.
    int luaopen_libtest(lua_State *L){ 
        luaL_newmetatable(L, "LuaBook.array"); // 创建元表 mt
        luaL_setfuncs(L, arraylib_m, 0);
        luaL_newlib(L, thelib);
        return 1;
    }
    asize = 1000
    a = lib.new(asize)
    print(#a)
    for i = 1, asize do
        a[i] = 1 / i --a.set(a, i, 1/i) -- or a:set(i, 1/i)
    end
    print(a[10])

         轻量用户数据( light userdata )代表指针,用其代替 userdata 会使开销少很多,内存必须自己管理,因为它不会被垃圾回收。

     
    #include "libtest.h"
    static int dir_iter(lua_State *L);
    
    static int dir_gc(lua_State *L)
    {
        DIR *dir = *(DIR**)lua_touserdata(L, 1);
        if (dir) {
            closedir(dir);
        }
        return 0;
    }
    
    int dirV2 (lua_State *L) {
        const char *path = luaL_checkstring(L, 1);
        DIR **dir = (DIR**)lua_newuserdata(L, sizeof(DIR*));
        *dir = NULL; // 预先初始化
        luaL_getmetatable(L, "LuaBook.dir");
        lua_setmetatable(L, -2);
        *dir = opendir(path);
        //创建并返回迭代函数;该函数唯一的upvalue,即代表目录的用户数据位于栈顶
        lua_pushcclosure(L, dir_iter, 1);
        return 1;
    }
    
    static int dir_iter(lua_State *L)
    {
        DIR *dir = *(DIR**)lua_touserdata(L, lua_upvalueindex(1));
        struct dirent *entry = readdir(dir);
        if (entry) {
            lua_pushstring(L, entry->d_name);
            return 1;
        } else {
            return 0; //遍历完成
        }
    }
    
    //Name of array mylib can be changed.
    static const struct luaL_Reg thelib[] = {
        {"ldir", dirV2},//dir},
        {NULL, NULL}
    };
    
    //Function name can not be changed.
    int luaopen_libtest(lua_State *L){ 
        luaL_newmetatable(L, "LuaBook.dir");
        lua_pushcfunction(L, dir_gc);
        lua_setfield(L, -2, "__gc");
        luaL_newlib(L, thelib);
        return 1;
    }
    for fname in lib.ldir(".") do
        print(fname)
    end

         

  • 相关阅读:
    Pentest_Mind-mapping 渗透测试思维导图
    pt-archiver归档数据丢失
    Vue笔记:bin-code-editor使用
    Vue笔记:Vue3 Table导出为Excel
    Go异步check简单示例
    flask_apscheduler定时任务组件使用
    组织沟通文化的变革与卓有成效的管理沟通
    业绩核能
    管理
    SAP MM 移动平均价管理的物料库存初始化导入的一个小问题
  • 原文地址:https://www.cnblogs.com/yyqng/p/14461269.html
Copyright © 2011-2022 走看看