Lua是一種嵌入式腳本語(yǔ)言,即Lua不是可以單獨(dú)運(yùn)行的程序,在實(shí)際應(yīng)用中,主要存在兩種應(yīng)用形式。第一種形式是,C/C++作為主程序,調(diào)用Lua代碼,此時(shí)可以將Lua看做“可擴(kuò)展的語(yǔ)言”,我們將這種應(yīng)用稱為“應(yīng)用程序代碼”。第二種形式是Lua具有控制權(quán),而C/C++代碼則作為L(zhǎng)ua的“庫(kù)代碼”。在這兩種形式中,都是通過(guò)Lua提供的C API完成兩種語(yǔ)言之間的通信的。
1. 基礎(chǔ)知識(shí):
C API是一組能使C/C++代碼與Lua交互的函數(shù)。其中包括讀寫(xiě)Lua全局變量、調(diào)用Lua函數(shù)、運(yùn)行一段Lua代碼,以及注冊(cè)C函數(shù)以供Lua代碼調(diào)用等。這里先給出一個(gè)簡(jiǎn)單的示例代碼:
int main(void)
{
const char* buff = "print(/"hello/")";
int error;
lua_State* L = luaL_newstate();
luaL_openlibs(L);
error = luaL_loadbuffer(L,buff,strlen(buff),"line") || lua_pcall(L,0,0,0);
int s = lua_gettop(L);
if (error) {
fprintf(stderr,"%s",lua_tostring(L,-1));
lua_pop(L,1);
}
lua_close(L);
return 0;
}
下面是針對(duì)以上代碼給出的具體解釋:
1). 上面的代碼是基于我的C++工程,而非C工程,因此包含的頭文件是lua.hpp,如果是C工程,可以直接包含lua.h。
2). Lua庫(kù)中沒(méi)有定義任何全局變量,而是將所有的狀態(tài)都保存在動(dòng)態(tài)結(jié)構(gòu)lua_State中,后面所有的C API都需要該指針作為第一個(gè)參數(shù)。
3). luaL_openlibs函數(shù)是用于打開(kāi)Lua中的所有標(biāo)準(zhǔn)庫(kù),如io庫(kù)、string庫(kù)等。
4). luaL_loadbuffer編譯了buff中的Lua代碼,如果沒(méi)有錯(cuò)誤,則返回0,同時(shí)將編譯后的程序塊壓入虛擬棧中。
5). lua_pcall函數(shù)會(huì)將程序塊從棧中彈出,并在保護(hù)模式下運(yùn)行該程序塊。執(zhí)行成功返回0,否則將錯(cuò)誤信息壓入棧中。
6). lua_tostring函數(shù)中的-1,表示棧頂?shù)乃饕?,棧底的索引值?,以此類推。該函數(shù)將返回棧頂?shù)腻e(cuò)誤信息,但是不會(huì)將其從棧中彈出。
7). lua_pop是一個(gè)宏,用于從虛擬棧中彈出指定數(shù)量的元素,這里的1表示僅彈出棧頂?shù)脑亍?br /> 8). lua_close用于釋放狀態(tài)指針?biāo)玫馁Y源。
2. 棧:
在Lua和C語(yǔ)言之間進(jìn)行數(shù)據(jù)交換時(shí),由于兩種語(yǔ)言之間有著較大的差異,比如Lua是動(dòng)態(tài)類型,C語(yǔ)言是靜態(tài)類型,Lua是自動(dòng)內(nèi)存管理,而C語(yǔ)言則是手動(dòng)內(nèi)存管理。為了解決這些問(wèn)題,Lua的設(shè)計(jì)者使用了虛擬棧作為二者之間數(shù)據(jù)交互的介質(zhì)。在C/C++程序中,如果要獲取Lua的值,只需調(diào)用Lua的C API函數(shù),Lua就會(huì)將指定的值壓入棧中。要將一個(gè)值傳給Lua時(shí),需要先將該值壓入棧,然后調(diào)用Lua的C API,Lua就會(huì)獲取該值并將其從棧中彈出。為了可以將不同類型的值壓入棧,以及從棧中取出不同類型的值,Lua為每種類型均設(shè)定了一個(gè)特定函數(shù)。
1). 壓入元素:
Lua針對(duì)每種C類型,都有一個(gè)C API函數(shù)與之對(duì)應(yīng),如:
API使用“索引”來(lái)引用棧中的元素,第一個(gè)壓入棧的為1,第二個(gè)為2,依此類推。我們也可以使用負(fù)數(shù)作為索引值,其中-1表示為棧頂元素,-2為棧頂下面的元素,同樣依此類推。
Lua提供了一組特定的函數(shù)用于檢查返回元素的類型,如:
3). 其它棧操作函數(shù):
除了上面給出的數(shù)據(jù)交換函數(shù)之外,Lua的C API還提供了一組用于操作虛擬棧的普通函數(shù),如:
lua_pushvalue(L,-4);
stackDump(L); //true 10 nil 'hello' true
lua_replace(L,3);
stackDump(L); //true 10 true 'hello'
lua_settop(L,6);
stackDump(L); //true 10 true 'hello' nil nil
lua_remove(L,-3);
stackDump(L); //true 10 true nil nil
lua_settop(L,-5);
stackDump(L); //true
lua_close(L);
return 0;
}
3. C API中的錯(cuò)誤處理:
1). C程序調(diào)用Lua代碼的錯(cuò)誤處理:
通常情況下,應(yīng)用程序代碼是以“無(wú)保護(hù)”模式運(yùn)行的。因此,當(dāng)Lua發(fā)現(xiàn)“內(nèi)存不足”這類錯(cuò)誤時(shí),只能通過(guò)調(diào)用“緊急”函數(shù)來(lái)通知C語(yǔ)言程序,之后在結(jié)束應(yīng)用程序。用戶可通過(guò)lua_atpanic來(lái)設(shè)置自己的“緊急”函數(shù)。如果希望應(yīng)用程序代碼在發(fā)生Lua錯(cuò)誤時(shí)不會(huì)退出,可通過(guò)調(diào)用lua_pcall函數(shù)以保護(hù)模式運(yùn)行Lua代碼。這樣再發(fā)生內(nèi)存錯(cuò)誤時(shí),lua_pcall會(huì)返回一個(gè)錯(cuò)誤代碼,并將解釋器重置為一致的狀態(tài)。如果要保護(hù)與Lua的C代碼,可以使用lua_cpall函數(shù),它將接受一個(gè)C函數(shù)作為參數(shù),然后調(diào)用這個(gè)C函數(shù)。
2). Lua調(diào)用C程序:
通常而言,當(dāng)一個(gè)被Lua調(diào)用的C函數(shù)檢測(cè)到錯(cuò)誤時(shí),它就應(yīng)該調(diào)用lua_error,該函數(shù)會(huì)清理Lua中所有需要清理的資源,然后跳轉(zhuǎn)回發(fā)起執(zhí)行的那個(gè)lua_pcall,并附上一條錯(cuò)誤信息。
新聞熱點(diǎn)
疑難解答
圖片精選