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

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相互调用)