国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項超值服

開通VIP
在可插入腳本的應(yīng)用程序中嵌入 Lua

2009 年 3 月 30 日

Lua 編程語言是一種小型的腳本語言,用于嵌入到其他程序中。通過使用 Lua 的 C API,可以編寫用于從 C 調(diào)用 Lua 以及從 Lua 調(diào)用 C 的非常干凈、簡單的代碼。對希望使用便捷的運(yùn)行時腳本語言的開發(fā)人員而言,這使他們可以輕松地實現(xiàn)腳本語言所需的基本 API 元素,然后在自己的應(yīng)用程序中使用 Lua 代碼。本文介紹 Lua 語言如何成為一種簡化常見開發(fā)任務(wù)的可行工具,并重點(diǎn)討論嵌入腳本語言的一些原因。

Lua 是一種小型腳本語言。它有多小呢?Lua 使用一個定制模式匹配特性,而不是 POSIX 正則表達(dá)式,因為一個完整的正則表達(dá)式實現(xiàn)比所有標(biāo)準(zhǔn)的 Lua 庫加起來還要大。Lua 提供的字符串匹配要簡單得多,它雖然沒有 POSIX 那么強(qiáng)大,但大小僅是 POSIX 的一小部分。

Lua 變量不是強(qiáng)類型的;雖然可以檢查一個值的類型,但是無法阻止一個變量的類型隨著時間而改變。這兩點(diǎn)正好適合腳本語言。Lua 的類型系統(tǒng)非常簡單,但很靈活。數(shù)組和關(guān)聯(lián)數(shù)組并合并為一種類型,即 table。string、number(只有浮點(diǎn)數(shù))、boolean 和特殊的 nil 類型都是基本類型。也許更有趣的是,函數(shù)(function)也是一種基本類型。就像其他類型一樣,可以方便地將函數(shù)賦給變量,不需要特殊的語法。另外,Lua 還支持定制的 userdata 對象,開發(fā)人員可以通過定義 userdata 對象來處理基本類型以外的其他類型。

對于習(xí)慣使用其他語言的程序員來說,Lua 最令人驚訝的地方是,只有 falsenil 被看作 false;任何非 boolean 類型的對象總是被看作 true。對于習(xí)慣 C 語言的人來說,這有些奇怪,因為在 C 中就可以用 1 和 0 作為 true 和 false。不過這很容易適應(yīng)。

Lua 是用可移植的 C 編寫的。它還可以與 C++ 一起使用,但是核心語言非常易于移植;雖然有少數(shù)特性需要借助宿主特性,但是 Lua 可脫離平臺依賴良好地運(yùn)行。Lua 無需進(jìn)行大量的 autoconf 測試,因為它嚴(yán)格遵從標(biāo)準(zhǔn)。Lua 是在 MIT 許可下發(fā)布的,可完全免費(fèi)用于任何用途,包括商業(yè)用途。(因此很多程序員隨意將它嵌入到應(yīng)用程序中)。

為什么嵌入一種語言?

嵌入一種腳本語言可以帶來很多的好處。我將使用初學(xué) Lua 時用的一個例子:Blizzard 的大規(guī)模多玩家在線 RPG,World of Warcraft(WoW)。WoW 的用戶界面完全使用 Lua 實現(xiàn)的;開發(fā)人員提供一些基本的 API 調(diào)用,以便真正與呈現(xiàn)引擎交互,并請求關(guān)于世界的數(shù)據(jù),然后使用 Lua 作為用戶界面代碼的核心。

這樣一來,將用戶界面代碼置于沙箱中,使之遠(yuǎn)離游戲本身的干擾,從而提高安全性和可靠性,這一點(diǎn)就變得容易得多。反過來,這又意味著 Blizzard 可以向玩家開放用戶界面,使玩家可以定制代碼,改變他們與游戲交互的方式。

通常,對于很多類型的任務(wù),與低級語言相比,腳本語言更易于引入到程序中。具有隱式分配和關(guān)聯(lián)數(shù)組特性,并支持垃圾收集的語言,通常有助于更 快地開發(fā)更簡單的代碼。它也許沒那么快,但是在很多情況下,這并不是問題;例如,用戶界面只需要比用戶的鍵盤輸入或鼠標(biāo)動作快就行了。

使用腳本語言有幾種方式。第一種方式,也是最簡單的方式,是使用它控制一個程序的行為,并使用 C 代碼作為一個真正用 Lua 編寫的程序的實現(xiàn)細(xì)節(jié)。第二種方法是主要用 C 編寫一個程序,然后使用嵌入的 Lua 來存儲和報告數(shù)據(jù)和配置。第三種方式,也是最靈活的方式,是混合上述兩種方式,使用 Lua 腳本編寫一些動作,而用 C 代碼管理其他部分。Lua 與 C 之間的接口非常簡單,因此這種方式非常流行。





回頁首


編寫引擎

對于主要用 Lua 編寫的程序,如果 CPU 時間主要用于逐個的操作,頂層控制的分量很輕,那么可以編寫一個引擎。這有助于將實現(xiàn)細(xì)節(jié)與高級設(shè)計相分離。用 Lua 而不是 C 來實現(xiàn)程序的核心邏輯可以大大減少開發(fā)時間。

對于這種類型的設(shè)計,顯然期望 Lua-to-C 接口主要由將從 Lua 調(diào)用的 C 函數(shù)的定義組成,并期望一旦開始執(zhí)行腳本,將來所有對 C 代碼的使用都從腳本中調(diào)用。





回頁首


可編寫腳本的配置文件

我所知道的每個程序員都至少編寫過一段這樣的代碼,這段代碼什么也不做,只是將配置值存儲到一個文件中,并在以后恢復(fù)那些值。(在我使用過的 配置文件當(dāng)中,Apple 的屬性列表也許是我最喜歡的)。但是,可以使用一種嵌入式腳本語言作為這些文件的格式,使用戶可以擁有壯觀的配置選項陣列。Ion 窗口管理器使用 Lua 作為配置文件,從而使用戶可以編寫強(qiáng)大而靈活的配置。

對用戶來說,最棒的是他們不再局限于簡單的賦值;用 Lua 表達(dá)的配置文件可以有注釋、條件等??梢蕴峁┮粋€優(yōu)先的 API,用于獲取可能影響配置選擇的數(shù)據(jù)。





回頁首


混合

Lua 與 C 之間可以來回嵌套,因為 Lua 解釋器是可重入的(reentrant)。如果 C 程序在一個腳本上調(diào)用解釋器,該腳本調(diào)用一個 C 函數(shù),而這個 C 函數(shù)又再次使用 Lua 解釋器,這是允許的。

World of Warcraft 基本上就是將這樣的模型用于它的用戶界面;用戶界面中的 Lua 調(diào)用可以反過來調(diào)用引擎,而引擎又將事件交付到用 Lua 編寫的用戶界面。這樣便得到一個靈活的界面,這樣的界面具有良好的隔離性和安全性,為用戶提供了很大的自主空間,并且緩沖區(qū)溢出或崩潰的風(fēng)險很小。在基于 C 的 API 中,嵌入的代碼幾乎都會導(dǎo)致崩潰;當(dāng)使用 Lua 界面時,如果用戶界面代碼導(dǎo)致崩潰,則存在需要修復(fù)的 bug。





回頁首


構(gòu)建和使用 Lua

構(gòu)建 Lua 很容易;只需運(yùn)行 make <platform>;如果不想依賴特定于平臺的特性,那么可以依靠 posix,甚至是 ansi。構(gòu)建后得到一個庫 liblua.a,可以將它鏈接到程序。恭喜!您已經(jīng)嵌入了 Lua。當(dāng)然,要想真正使用它,還需要做一些工作。

Lua 的可重入性源于將所有解釋器狀態(tài)保存在一個對象中;可以有多個解釋器,它們之間不共享變量,也沒有共享的全局項。為了與 Lua 交互,必須從創(chuàng)建一個 Lua 狀態(tài)開始:

lua_State *l;
l = lua_open();

如果 lua_open() 調(diào)用失敗,則返回一個 null 指針。否則,就有了一個有效的 Lua 解釋器狀態(tài)。當(dāng)然,如果沒有庫,還是不夠??梢允褂?luaL_openlibs() 函數(shù)添加標(biāo)準(zhǔn)庫:

luaL_openlibs(l);

Lua 狀態(tài)現(xiàn)在可以執(zhí)行代碼了。下面是一個程序中的一個循環(huán),它只是執(zhí)行作為 Lua 代碼的參數(shù):


清單 1. 將參數(shù)作為 Lua 代碼執(zhí)行

                        for (i = 1; i < argc; ++i) {                        if (luaL_loadbuffer(l, argv[i], strlen(argv[i]), "argument")) {                        fprintf(stderr, "lua couldn't parse '%s': %s.\n",                        argv[i], lua_tostring(l, -1));                        lua_pop(l, 1);                        } else {                        if (lua_pcall(l, 0, 1, 0)) {                        fprintf(stderr, "lua couldn't execute '%s': %s.\n",                        argv[i], lua_tostring(l, -1));                        lua_pop(l, 1);                        } else {                        lua_pop(l, lua_gettop(l));                        }                        }                        }                        

luaL_loadbuffer() 函數(shù)將一個腳本編譯成 Lua 代碼。如果有語法錯誤,從這里可以觀察到失敗。錯誤消息被返回到棧上。否則,使用 lua_pcall() 函數(shù)執(zhí)行編譯后的代碼。同樣,如果有錯誤,錯誤消息被返回到棧上。注意,每個對 Lua 或關(guān)于 Lua 的 C 語言調(diào)用都帶有一個 Lua 狀態(tài)作為一個參數(shù);沒有缺省的狀態(tài)。

理解 Lua 棧

Lua 解釋器使用一個棧接口來與調(diào)用代碼通信。由 C 代碼將發(fā)送到 Lua 代碼的數(shù)據(jù) push 到棧上;Lua 解釋器返回的響應(yīng)也被 push 到棧上。如果傳遞給 luaL_loadbuffer() 的代碼有錯誤,則錯誤消息被 push 到棧上。

棧上的項有類型和值。lua_type() 函數(shù)查詢一個對象的類型;lua_to<type>() 函數(shù)(例如 lua_tostring())產(chǎn)生被強(qiáng)制轉(zhuǎn)換成特定 C 類型的值。用 Lua 編寫的代碼總是嚴(yán)格遵從棧模型;但是,C 代碼則可以探查棧的其余部分,甚至可以在棧中插入值。

這種接口雖然簡單,但是其功能出奇強(qiáng)大。待執(zhí)行的代碼都被同等對待;首先被 push 到棧上,然后等待 lua_pcall() 函數(shù)來執(zhí)行它。

使用 lua_pcall()

除了要在其上面進(jìn)行操作的 Lua 狀態(tài)外,lua_pcall() 函數(shù)還帶有 3 個參數(shù)。待執(zhí)行的代碼并非這些參數(shù)之一;這個代碼由 luaL_loadbuffer() 或獲得代碼的其他函數(shù) push 到棧上。實際上,lua_pcall() 以傳遞給要執(zhí)行的代碼的棧參數(shù)的數(shù)量、期望返回的結(jié)果的數(shù)量以及一個錯誤處理程序為參數(shù),最后一個參數(shù)是可選的。要調(diào)用一個函數(shù),首先要 push 該函數(shù),然后 push 該函數(shù)??帶的參數(shù)(按順序)。返回的參數(shù)以同樣的順序 push。返回的第一個值在下,最后一個值在上。

無論是對于發(fā)送參數(shù)還是獲取結(jié)果值,Lua 都自動更正值的數(shù)量,以便與傳遞給 lua_pcall() 的數(shù)量匹配;如果沒有提供足夠的值,那么剩下的參數(shù)以 nil 值填充,如果有額外的值,多出的值被自動丟棄。(這與 Lua 在多個賦值操作上的行為是一樣的)。

如果提供錯誤處理程序,那么它應(yīng)該是用于處理任何發(fā)生的錯誤的 Lua 代碼在棧上的索引。對于這篇概述性的文章,我不會詳細(xì)討論錯誤處理;但是要知道兩點(diǎn),首先,錯誤處理是存在的,其次,錯誤處理是在 Lua 中進(jìn)行的。這樣非常方便。





回頁首


將 C 嵌入到 Lua 中

用 C 編寫 Lua 使用的函數(shù)非常容易。如果您曾經(jīng)編寫過嵌入到其他腳本語言中的代碼,那么您也許會感到震驚。下面是一個 C 函數(shù),它接收一個數(shù)字 x,返回 x + 1


清單 2. 供 Lua 使用的 C 函數(shù)

                        int                        l_ink(lua_State *L) {                        int x;                        if (lua_gettop(L) >= 0) {                        x = (int) lua_tonumber(L, -1);                        lua_pushnumber(L, x + 1);                        }                        return 1;                        }                        

該函數(shù)是借助一個 Lua 狀態(tài)參數(shù)來調(diào)用的;同樣,C 與 Lua 之間的所有交互都是通過 Lua 狀態(tài)棧發(fā)生的。返回值是該函數(shù) push 到棧上的對象的數(shù)量。為了使 Lua 可以使用該函數(shù),必須做兩件事。首先是創(chuàng)建一個表示該函數(shù)的 Lua 對象,其次是為它提供一個名稱:

lua_pushcfunction(L, l_ink);
lua_setglobal(L, "ink");

可以使用 lua_pushcfunction() 將一個 C 函數(shù)指針轉(zhuǎn)換成一個內(nèi)部 Lua 對象。當(dāng)然,這個對象被 push 到棧上。然后,lua_setglobal() 函數(shù)將棧頂?shù)闹蒂x給一個有名稱的全局變量。由于函數(shù)在 Lua 中就是值,所以這實際上就是創(chuàng)建一個回調(diào)函數(shù)。

棧接口大大簡化了這一點(diǎn);在此,沒有必要定義或聲明函數(shù)所帶的參數(shù)。對于如何調(diào)用代碼,Lua 是非常靈活的。不過您如果愿意,也可以進(jìn)行更仔細(xì)的檢查。luaL_checknumber() 函數(shù)還可以用于檢查參數(shù),并且還能打印包含信息的錯誤消息,以及中斷函數(shù)的執(zhí)行。





回頁首


結(jié)束語

將 Lua 嵌入到其他語言(特別是 C)編寫的代碼中非常簡單,因此常常使用 Lua 提高用其他語言編寫的程序的功能性。相對于發(fā)明自己的配置語言或編寫自己的表達(dá)式解析器而言,這是一個切實可行的替代方案。

習(xí)慣于使用更大型腳本語言的程序員會對一些事情感到驚訝。首先,設(shè)置一個 Lua 解析器的成本非常小;如果想在沙箱中運(yùn)行一些東西,則可以直接這樣做。雖然 Lua 的很多安全性特性我還沒接觸過,但是應(yīng)該清楚,即使在一個 Lua 狀態(tài)中都可以有效地實現(xiàn)沙箱。

僅返回關(guān)于程序狀態(tài)的數(shù)據(jù)的函數(shù)可以成為配置腳本和數(shù)據(jù)的好工具。如果您想開始用 Lua 編寫某種頂層的邏輯,那很容易,并且非常有效。很多腳本語言勉強(qiáng)能夠與其他語言的代碼緊密協(xié)作,而 Lua 則是完全為了與其他語言緊密協(xié)作而設(shè)計的。

本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊舉報。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
c++調(diào)用lua腳本1(平臺windows)
Lua“控制”C | 果凍想
Redis源碼學(xué)習(xí):Lua腳本
Step By Step(Lua調(diào)用C函數(shù))(轉(zhuǎn))
動態(tài)調(diào)用動態(tài)語言,第 1 部分: 引入 Java 腳本 API
基于Lua腳本語言的嵌入式UART通信方案設(shè)計
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服