Lua —— 轻量小巧脚本语言,支持与C相互调用
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 |