Lua 技術備註 4

跨語言工作的一組精簡 API,或使用四個簡單呼叫來使用 Lua

作者:Reuben Thomas

摘要

讓 Lua 與語言 L 互動的顯然方法是在 L 中實作 Lua API,但這對實作者來說很困難,而且會讓 L 程式設計師負擔冗長的語法。一個更簡單的解決方案是在 L 中實作 lua_openlua_closelua_dobufferlua_register,並擴充 lua_register 以執行跨語言封送。然後可以在 Lua 中提供其他功能。

問題

Lua API 允許從 C 完全控制 Lua 狀態。但是,如果您想與另一種語言 L 互動怎麼辦?假設 L 可以與 C 互動,顯然的方法是將 C API 反映到 L 中。然而,這是一個相當令人生畏的前景,因為 Lua API 很大,而且有一些隱藏的細微差別。此外,雖然它是一個編寫 Lua 擴充程式庫和工具的良好媒介,但它並不會自然而然地產生方便 L 程式設計師的語法;Lua 手冊顯示(請參閱 4.0 版第 26 頁的 5.12 節)Lua 陳述式

a,b = f("how", t.x, 4)

會變成對 Lua API 的十次呼叫。

解決方案

有一個更簡單的方法:使用 Lua 的第一條規則(「在 Lua 中執行」)

lua_dostring(S, "a,b = f(\"how\", t.x, 4)");

其中 S 是要執行程式碼的狀態。事實上,lua_dostring 唯一做不到的事情是從 Lua 取回值,並允許 Lua 呼叫 L。這兩個都可以使用 lua_register 來達成。

因此,語言互動所需的一切就是 lua_dostringlua_register,加上 lua_openlua_close 以允許建立和銷毀 Lua 狀態。此外,最好使用 lua_dobuffer 而不是 lua_dostring,因為它也可以處理預編譯的程式碼。

但是等等!在 C API 中,lua_register 沒有說明要註冊的函式的引數或結果類型;這些必須透過檢查和操作 Lua 堆疊來處理。一個殘酷但簡單的解決方案是讓 lua_register 指定引數和回傳值的類型和數量,並且只允許自然對應到 L 的類型。

精簡 API 的函式最終清單是

案例研究:Lua 到 OPL

將 Lua 移植到 EPOC 時,Symbian 的行動裝置作業系統,例如 PDA,我想提供掛鉤到作業系統功能,例如 Eikon GUI。EPOC 是以 C++ 為基礎,看起來很有希望,但由於空間因素,其函式庫不包含任何符號資訊,因此無法依據名稱進行執行時期動態連結。不想要使用 tolua,我決定將 Lua 繫結到 OPL,EPOC 的解譯 BASIC 類似 RAD 語言,它同時支援 EPOC,包括廣泛的 OPX(以 C++ 實作的 OPL 函式庫),並允許依據名稱動態呼叫程序。

OPL 有四個基本類型:16 位元和 32 位元整數、64 位元浮點數和字串。16 位元整數表示為 %、32 位元整數表示為 &、字串表示為 $,浮點數則不表示。OPL 支援 C 類似函式原型,例如

foo&:(a,b%,c$)

foo 是函式的名稱。& 表示它傳回 32 位元整數(所有 OPL 函式傳回一個值,如果沒有明確的 RETURN 陳述式,預設為零或空字串)。冒號表示 foo 是函式。接下來是選用的引數清單;在這個案例中,有三個引數:浮點數 a、16 位元整數 b% 和字串 c$。(字串長度最多 255 個字元;在此 API 中,較長的字串無法直接與 Lua 交換。)

因此,我建立一個小型 OPX,提供下列 OPL 函式

Lua&: 看起來比 LuaDoBuffer&: 更好的名稱,因為它既貼切(Lua&: 是執行一些 Lua 的函式),又是四個程序中很可能使用最廣泛的簡短好名稱。當從 Lua 呼叫由 LuaRegister: 註冊的 OPL 函式時,引數會自動轉換為 OPL 類型,而結果類型會轉換回來。檢查整數引數是否在範圍內是程式設計師的責任。

一個精簡的 API 是否足夠?

乍看之下,這個介面似乎非常受限。例如,沒有簡單的方法可以評估 Lua 表達式並將其結果傳回 OPL,也無法在 OPL 中遍歷 Lua 表格。這是故意的:加入這些功能會使 API 複雜化,而省略這些功能會鼓勵程式設計師僅使用 OPL 提供函式庫常式給 Lua。畢竟,將 Lua 連結到 OPL 的主要動機是能夠存取 EPOC,而無需先為 Lua 編寫大量 C++ 函式庫。

然而,在某些情況下,我可能想要使用另一種語言撰寫大部分應用程式,因為它的應用程式網域屬性(例如 SQL 或 Prolog)。此外,我似乎將 Lua 從其預期的用途(應用程式擴充語言)提升為撰寫應用程式的主要語言。

實際上,這裡沒有衝突。不要將 Lua 視為應用程式擴充語言,而是將它視為膠水語言,將以其他語言撰寫的程式片段結合在一起。應用程式功能的核心通常會以其他語言 L 實作,可能是為了速度而使用 C,或使用某些特定網域語言。透過將此核心結構化為函式庫,L 程式設計人員可以專注於在 L 中提供應用程式原語,而不用擔心將它們綁在一起;L 可能不適合這樣做。然後,應用程式可以實作為 Lua 層,位於一系列函式庫之上;這將程式設計特定網域原語與設定特定應用程式的不同考量分開,這使得應用程式更容易撰寫,並促進 Lua 和 L 程式碼的重複使用。

如果真的有必要在 L 中實作 Lua API 的其他部分,那麼,只要不是為了效能考量,仍然可以使用 Lua 搭配 L 回呼來實作必要的功能。事實上,可以撰寫 Lua API 的完整 Lua 實作,然後與 Lua 透過精簡 API 介接的任何語言一起使用。

結論

Lua 可以透過非常簡單的 API 連接到其他語言,這個 API 主要為標準 C API 的子集。只要目標語言可以與 C 互通,就可以快速實作,並提供所有必要的功能,以便使用 Lua 和目標語言的混合來撰寫應用程式。精簡 API 中的一些看似限制,實際上有助於撰寫更可重複使用的程式碼。


最後更新時間:2002 年 8 月 12 日星期一下午 3:49:10 美東時間,作者:lhf