Lua —— 輕量小巧腳本語言,支持與C相互調(diào)用
Lua 是巴西里約熱內(nèi)盧天主教大學(xué)(Pontifical Catholic University of Rio de Janeiro)里的一個研究小組于 1993 年開發(fā)宪巨。
Lua 用標(biāo)準(zhǔn) C 語言編寫并開放源代碼轴术。
優(yōu)勢:
- 輕量級:使用標(biāo)準(zhǔn)C語言編寫,編譯后僅僅100k+巫玻,可以很方便地加入嵌入式程序中丛忆。
- 可擴(kuò)展:Lua 提供非常易于使用的擴(kuò)展接口和機(jī)制。由宿主語言(通常是 C仍秤、C++)提供功能熄诡,Lua 如同內(nèi)置功能一樣進(jìn)行調(diào)用。
- 支持面向過程編程和函數(shù)式編程
- 自動內(nèi)存管理诗力。只提供一種通用類型的表(table)凰浮,可以用來實現(xiàn)數(shù)組,哈希表苇本,集合袜茧,對象等。
- 提供多線程(協(xié)同進(jìn)程)支持
但 Lua 目前沒有提供強(qiáng)大的庫瓣窄,不適合作為開發(fā)獨立應(yīng)用程序的語言使用惫周。
Lua 數(shù)據(jù)類型
數(shù)據(jù)類型 | 說明 |
---|---|
nil | 表示一個無效的值(類似于null、NULL康栈、false等) |
boolean | 布爾型,true 或 false |
number | 雙精度類型的實浮點數(shù) |
string | 字符串有一對雙引號("")或單引號('')表示 |
function | 由 C 或 Lua 編寫的函數(shù) |
userdata | 表示任意存儲在變量中的 C 數(shù)據(jù)結(jié)構(gòu) |
thread | 表示執(zhí)行獨立的線路喷橙,用于執(zhí)行協(xié)同程序 |
table | Lua 中的表(table)其實是一個 "關(guān)聯(lián)數(shù)組"(associative arrays)啥么,數(shù)組的索引可以是數(shù)字或者是字符串。在 Lua 里贰逾,table 的創(chuàng)建是通過 "構(gòu)造表達(dá)式" 來完成悬荣,最簡單構(gòu)造表達(dá)式是 {},用來創(chuàng)建一個空表疙剑。 |
測試數(shù)據(jù)類型(使用 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 語言交互是其最大的魅力之一氯迂。
運(yùn)用 C 的庫擴(kuò)展了其強(qiáng)大的功能践叠。
C 與 Lua 交互的部分稱為 C API。C API 是一個 C 代碼與 Lua 進(jìn)行交互的函數(shù)集嚼蚀。主要組成部分為:
- 讀寫 Lua 全局變量的函數(shù)
- 調(diào)用 Lua 函數(shù)的函數(shù)
- 運(yùn)行 Lua 代碼片段的函數(shù)
- 注冊 C 函數(shù)然后可以在 Lua 中被調(diào)用的函數(shù)
C 與 Lua 之間采用一個虛擬的棧進(jìn)行通信禁灼,這樣巧妙地解決了數(shù)據(jù)類型匹配問題和內(nèi)存管理不一致的問題。
所有 C 與 Lua 之間的數(shù)據(jù)交換也都通過這個棧來完成轿曙。
Lua 以一個嚴(yán)格的 LIFO (后進(jìn)先出)規(guī)則來操作棧弄捕。
1. C 調(diào)用 Lua
初始化和結(jié)束接口。
lua_State *L = lua_open(); // 創(chuàng)建 lua_State 堆棧(用于交換數(shù)據(jù))导帝。
luaL_openlibs(L); // 初始化堆棧
...
lua_close(L); // 釋放
C 與 Lua 的交互方式可以是通過 luaL_dostring (直接運(yùn)行 Lua 代碼)或 luaL_dofile (直接運(yùn)行 Lua 文件)守谓。
如 Lua 文件
// 文件名: test_lua.lua
print("This is Lua !")
可以執(zhí)行:
// 可以直接執(zhí)行代碼
const char * buf = "print("This is Lua !")";
luaL_dostring(L, buf);
// 也可以直接執(zhí)行文件
luaL_dofile(L, "test_lua.lua");
由于沒有 include Lua 文件的機(jī)制。
如果需要訪問 Lua 文件內(nèi)的數(shù)據(jù)或接口您单,需要調(diào)用 luaL_loadfile 加載 Lua 文件斋荞,后續(xù)才能進(jìn)行讀取接口或數(shù)據(jù)。
下面以讀取 test_lua.lua 文件為例:
int main()
{
lua_State *L = lua_open(); // 創(chuàng)建 lua_State 堆棧(用于交換數(shù)據(jù))虐秦。
luaL_openlibs(L); // 初始化堆棧
if(luaL_loadfile(L, filename)) // 返回 1 則加載出錯
{
return -1;
}
...
lua_close(L); // 釋放
return 0;
}
調(diào)用 Lua 函數(shù)的方法
Lua 代碼:
function add(a, b, c)
local sum=a+b
return sum,c
end
C 調(diào)用 Lua 函數(shù):
lua_getglobal(L, "add"); // 在Lua中平酿,函數(shù)等同于變量,所以你可以這樣來取得這個函數(shù)
lua_pushnumber(L, 100); // 將參數(shù)壓棧羡疗,對應(yīng) a
lua_pushnumber(L, 20); // 將參數(shù)壓棧染服,對應(yīng) b
lua_pushstring(L, "test add function"); // 將參數(shù)壓棧,對應(yīng) c
lua_pcall(L, 3, 2, 0); // 調(diào)用函數(shù)叨恨,3個參數(shù)柳刮,2個返回值,錯誤處理函數(shù)(0表示沒有痒钝,其它表示處理函數(shù)在棧的索引)秉颗。
const char * result1 = lua_tostring(L, -1); // 返回值,對應(yīng)返回的 c (按返回值入棧順序送矩,先是 sum, 后是 c, 所以 c 的順序為 -1)
int result2 = lua_tonumber(L, -2); // 返回值蚕甥,對應(yīng)返回的 sum
2. Lua 調(diào)用 C
C 函數(shù):
int lua_strlen(lua_State *L) // Lua 用棧進(jìn)行數(shù)據(jù)傳遞,所以參數(shù)用棧就可以了
{
const char * ptr = lua_tostring(L, -1); // 獲取輸入的第一個參數(shù)
int len = strlen(ptr); // 計算字符串長度
lua_pushnumber(L, len); // 計算結(jié)果入棧
return 1;
}
int main()
{
lua_State *L = lua_open();
luaL_openlibs(L);
...
lua_register(L, "lua_strlen", lua_strlen); // 需要注冊一下栋荸,聲明暴露給 Lua 調(diào)用的接口和名稱菇怀。
...
lua_close(L);
return 0;
}
Lua 調(diào)用文件:
function calc_length(s)
local len=lua_strlen(s)
return len
end
附錄:C API 一些接口說明
接口 | 說明 | 部分參數(shù)說明 |
---|---|---|
lua_State* lua_open(); |
獲取一個新的 Lua 狀態(tài)機(jī) | 如果內(nèi)存不足返回 NULL |
lua_State *lua_newstate (lua_Alloc f, void *ud); |
獲取一個新的 Lua 狀態(tài)機(jī) | |
lua_State* lua_open(); |
獲取一個新的 Lua 狀態(tài)機(jī) | 參數(shù) f 指定內(nèi)存分配函數(shù),參數(shù) ud 是傳給 f 函數(shù)的指針晌块。如果內(nèi)存不足返回 NULL |
void lua_close(lua_State *L); |
銷毀 Lua 狀態(tài)機(jī)所有對象爱沟,回收分配的內(nèi)存 | |
void luaL_openlibs(lua_State *L); |
在給定的 Lua 狀態(tài)機(jī)中打開所有的標(biāo)準(zhǔn) Lua 庫 | |
int luaL_dofile(lua_State *L, char *lua_script); |
加載并執(zhí)行給定的 Lua 文件 | 成功返回 0,錯誤返回 1 |
int luaL_dostring (lua_State *L, const char *str); |
加載并執(zhí)行給定 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); |
返回棧頂元素的索引(也即元素個數(shù)) | |
void lua_call (lua_State *L, int nargs, int nresults); |
調(diào)用函數(shù) | 參數(shù) nargs 指定函數(shù)參數(shù)個數(shù)钝尸,參數(shù) nresults 指定返回值個數(shù)括享。 |
int lua_pcall (lua_State *L, int nargs, int nresults, int errfunc); |
以保護(hù)模式調(diào)用函數(shù)搂根,如果發(fā)生錯誤,捕捉它铃辖,并將錯誤消息壓入棧剩愧,然后返回錯誤碼。 | 參數(shù) nargs 指定函數(shù)參數(shù)個數(shù)澳叉,參數(shù) nresults 指定返回值個數(shù)隙咸,參數(shù) errfunc 是錯誤處理函數(shù)在棧的索引(沒有時為 0 )。 |
int lua_type (lua_State *L, int index); |
返回第 index 個數(shù)據(jù)的類型ID | 返回類型ID為:LUA_TNIL, LUA_TNUMBER, LUA_TBOOLEAN, LUA_TSTRING, LUA_TTABLE, LUA_TFUNCTION, LUA_TUSERDATA, LUA_TTHREAD, and LUA_TLIGHTUSERDATA |