美文网首页
Lua —— 轻量小巧脚本语言,支持与C相互调用

Lua —— 轻量小巧脚本语言,支持与C相互调用

作者: ZONE画派 | 来源:发表于2019-01-13 18:14 被阅读14次

    Lua —— 轻量小巧脚本语言,支持与C相互调用

    Lua 语言 logo

    Lua 是巴西里约热内卢天主教大学(Pontifical Catholic University of Rio de Janeiro)里的一个研究小组于 1993 年开发。

    Lua 用标准 C 语言编写并开放源代码。

    优势:

    • 轻量级:使用标准C语言编写,编译后仅仅100k+,可以很方便地加入嵌入式程序中。
    • 可扩展:Lua 提供非常易于使用的扩展接口和机制。由宿主语言(通常是 C、C++)提供功能,Lua 如同内置功能一样进行调用。
    • 支持面向过程编程和函数式编程
    • 自动内存管理。只提供一种通用类型的表(table),可以用来实现数组,哈希表,集合,对象等。
    • 提供多线程(协同进程)支持

    但 Lua 目前没有提供强大的库,不适合作为开发独立应用程序的语言使用。

    Lua 数据类型

    数据类型 说明
    nil 表示一个无效的值(类似于null、NULL、false等)
    boolean 布尔型,true 或 false
    number 双精度类型的实浮点数
    string 字符串有一对双引号("")或单引号('')表示
    function 由 C 或 Lua 编写的函数
    userdata 表示任意存储在变量中的 C 数据结构
    thread 表示执行独立的线路,用于执行协同程序
    table Lua 中的表(table)其实是一个 "关联数组"(associative arrays),数组的索引可以是数字或者是字符串。在 Lua 里,table 的创建是通过 "构造表达式" 来完成,最简单构造表达式是 {},用来创建一个空表。

    测试数据类型(使用 type ):


    print(type("Hello world"))      --> string
    print(type(10.4*3))             --> number
    print(type(2))                  --> number
    print(type(3.14))               --> number
    print(type(print))              --> function
    print(type(type))               --> function
    print(type(true))               --> boolean
    print(type(nil))                --> nil
    print(type(type(nil)))          --> string
    

    Lua 与 C 语言的交互

    Lua 能与 C 语言交互是其最大的魅力之一。

    运用 C 的库扩展了其强大的功能。

    C 与 Lua 交互的部分称为 C API。C API 是一个 C 代码与 Lua 进行交互的函数集。主要组成部分为:

    • 读写 Lua 全局变量的函数
    • 调用 Lua 函数的函数
    • 运行 Lua 代码片段的函数
    • 注册 C 函数然后可以在 Lua 中被调用的函数

    C 与 Lua 之间采用一个虚拟的栈进行通信,这样巧妙地解决了数据类型匹配问题和内存管理不一致的问题。

    所有 C 与 Lua 之间的数据交换也都通过这个栈来完成。

    Lua 以一个严格的 LIFO (后进先出)规则来操作栈。

    1. C 调用 Lua

    初始化和结束接口。


    lua_State *L = lua_open();    // 创建 lua_State 堆栈(用于交换数据)。
    luaL_openlibs(L);             // 初始化堆栈
    
    ...
    
    lua_close(L);                 // 释放
    

    C 与 Lua 的交互方式可以是通过 luaL_dostring (直接运行 Lua 代码)或 luaL_dofile (直接运行 Lua 文件)。

    如 Lua 文件


    // 文件名: test_lua.lua
    print("This is Lua !")
    

    可以执行:


    // 可以直接执行代码
    const char * buf = "print("This is Lua !")";
    luaL_dostring(L, buf);
    
    // 也可以直接执行文件
    luaL_dofile(L, "test_lua.lua");
    

    由于没有 include Lua 文件的机制。

    如果需要访问 Lua 文件内的数据或接口,需要调用 luaL_loadfile 加载 Lua 文件,后续才能进行读取接口或数据。

    下面以读取 test_lua.lua 文件为例:


    int main()
    {
        lua_State *L = lua_open();        // 创建 lua_State 堆栈(用于交换数据)。
        luaL_openlibs(L);                 // 初始化堆栈
    
        if(luaL_loadfile(L, filename))    // 返回 1 则加载出错
        {
            return -1;
        }
        
        ...
    
        lua_close(L);                     // 释放
        return 0;
    }
    

    调用 Lua 函数的方法

    Lua 代码:


    function add(a, b, c)
        local sum=a+b
        return sum,c
    end
    

    C 调用 Lua 函数:


    lua_getglobal(L, "add");                      // 在Lua中,函数等同于变量,所以你可以这样来取得这个函数
    lua_pushnumber(L, 100);                       // 将参数压栈,对应 a
    lua_pushnumber(L, 20);                        // 将参数压栈,对应 b
    lua_pushstring(L, "test add function");       // 将参数压栈,对应 c
    
    lua_pcall(L, 3, 2, 0);                        // 调用函数,3个参数,2个返回值,错误处理函数(0表示没有,其它表示处理函数在栈的索引)。
    
    const char * result1 = lua_tostring(L, -1);   // 返回值,对应返回的 c (按返回值入栈顺序,先是 sum, 后是 c, 所以 c 的顺序为 -1)
    int result2 = lua_tonumber(L, -2);            // 返回值,对应返回的 sum
    

    2. Lua 调用 C

    C 函数:


    int lua_strlen(lua_State *L)                    // Lua 用栈进行数据传递,所以参数用栈就可以了
    {
        const char * ptr = lua_tostring(L, -1);     // 获取输入的第一个参数
        int len = strlen(ptr);                      // 计算字符串长度
        lua_pushnumber(L, len);                     // 计算结果入栈
        return 1;
    }
    
    int main()
    {
        lua_State *L = lua_open();
        luaL_openlibs(L);
    
        ...
    
        lua_register(L, "lua_strlen", lua_strlen);  // 需要注册一下,声明暴露给 Lua 调用的接口和名称。
    
        ...
    
        lua_close(L);
        return 0;
    }
    

    Lua 调用文件:


    function calc_length(s)
        local len=lua_strlen(s)
        return len
    end
    

    附录:C API 一些接口说明

    接口 说明 部分参数说明
    lua_State* lua_open(); 获取一个新的 Lua 状态机 如果内存不足返回 NULL
    lua_State *lua_newstate (lua_Alloc f, void *ud); 获取一个新的 Lua 状态机
    lua_State* lua_open(); 获取一个新的 Lua 状态机 参数 f 指定内存分配函数,参数 ud 是传给 f 函数的指针。如果内存不足返回 NULL
    void lua_close(lua_State *L); 销毁 Lua 状态机所有对象,回收分配的内存
    void luaL_openlibs(lua_State *L); 在给定的 Lua 状态机中打开所有的标准 Lua 库
    int luaL_dofile(lua_State *L, char *lua_script); 加载并执行给定的 Lua 文件 成功返回 0,错误返回 1
    int luaL_dostring (lua_State *L, const char *str); 加载并执行给定 string 成功返回 0,错误返回 1
    int luaL_loadfile (lua_State *L, const char *filename); 从文件加载 chunk 成功返回 0,错误返回 1
    int luaL_loadstring (lua_State *L, const char *s); 从字符串加载 chunk 成功返回 0,错误返回 1
    void lua_pop(lua_State *L, int n); 从栈顶弹出 n 个元素
    int lua_gettop (lua_State *L); 返回栈顶元素的索引(也即元素个数)
    void lua_call (lua_State *L, int nargs, int nresults); 调用函数 参数 nargs 指定函数参数个数,参数 nresults 指定返回值个数。
    int lua_pcall (lua_State *L, int nargs, int nresults, int errfunc); 以保护模式调用函数,如果发生错误,捕捉它,并将错误消息压入栈,然后返回错误码。 参数 nargs 指定函数参数个数,参数 nresults 指定返回值个数,参数 errfunc 是错误处理函数在栈的索引(没有时为 0 )。
    int lua_type (lua_State *L, int index); 返回第 index 个数据的类型ID 返回类型ID为:LUA_TNIL, LUA_TNUMBER, LUA_TBOOLEAN, LUA_TSTRING, LUA_TTABLE, LUA_TFUNCTION, LUA_TUSERDATA, LUA_TTHREAD, and LUA_TLIGHTUSERDATA

    相关文章

      网友评论

          本文标题:Lua —— 轻量小巧脚本语言,支持与C相互调用

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