簡便手册程序庫加載 |
|
本文介紹一種方法,讓您可以透過在程式原始碼中僅變更 5 行 C 語法,就能手動將所有 Lua 5.1 函式從共用程序庫載入到您的應用程式中!如果您已經手動載入一些共用程序庫(.dll 或 .so),您可以跳過接下來的四個引言章節。您可以在此處找到原始碼:[lua_dyn.zip]
*.dll
)。在 Unix 中,它們通常稱為共用物件(*.so
)或共用程序庫(*.sa
)。兩者都指的是可執行檔案的相同本質,而我們從現在起將它稱為 SL(共用程序庫)。SL 包含已編譯且連結的機器碼,會將公共函式匯出到應用程式,但本身沒有 main()
函式,因此無法直接執行。相反地,作業系統會將 SL 載入應用程式的記憶體空間中,然後應用程式就可以看到匯出的 SL 函式和變數,就像它們在應用程式內部一樣。
__declspec(dllimport)
函式前綴)來編譯應用程式,並連結到特殊的靜態程序庫(在 Windows 中為 *.lib
,在 Unix 中為 *.a
),這樣就完成了。在手動模式中,應用程式本身必須載入 SL(在 Windows 中使用 LoadLibrary
或在 POSIX 系統中使用 dlopen
),使用 GetProcAddress
或 dlsym
匯出每個需要的函式,並在完成後使用 FreeLibrary
或 dlclose
關閉 SL。一般來說,您無法使用 SL 標頭檔,但必須對函式原型進行 typedef
,實例化其變數,並在呼叫匯出的函式之前將 GetProcAddress
的結果傳遞給此變數。
Hello World!
範例。字串將會由 Lua 5.1 的直譯器輸出,並在 luaL_dostring
函數內部執行的。首先,示範使用自動 SL 載入的範例。你必須將它連結到相關的靜態函式庫(類似 -llua
)。#include "lua.h" #include "lauxlib.h" #include "lualib.h" int main(int argc, char* argv[]) { lua_State* L; L = lua_open(); luaL_openlibs(L); luaL_dostring(L, "print 'Hello World'"); lua_close(L); return 0; }
#include <windows.h> #include "lua.h" typedef lua_State * (__cdecl *luaL_newstate_t) (void); typedef void (__cdecl *luaL_openlibs_t) (lua_State *L); typedef int (__cdecl *luaL_loadstring_t) (lua_State *L, const char *s); typedef int (__cdecl *lua_pcall_t) (lua_State *L, int nargs, int nresults, int errfunc); typedef void (__cdecl *lua_close_t) (lua_State *L); luaL_newstate_t luaL_newstate_ptr; luaL_openlibs_t luaL_openlibs_ptr; luaL_loadstring_t luaL_loadstring_ptr; lua_pcall_t lua_pcall_ptr; lua_close_t lua_close_ptr; int main(int argc, char* argv[]) { lua_State* L; HMODULE module = LoadLibrary("lua5.1.dll"); if(module == NULL) return 1; luaL_newstate_ptr = (luaL_newstate_t) GetProcAddress(module, "luaL_newstate"); luaL_openlibs_ptr = (luaL_openlibs_t) GetProcAddress(module, "luaL_openlibs"); luaL_loadstring_ptr = (luaL_loadstring_t)GetProcAddress(module, "luaL_loadstring"); lua_pcall_ptr = (lua_pcall_t) GetProcAddress(module, "lua_pcall"); lua_close_ptr = (lua_close_t) GetProcAddress(module, "lua_close"); if(luaL_newstate_ptr == NULL || luaL_openlibs_ptr == NULL || lua_close_ptr == NULL || luaL_loadstring_ptr == NULL || lua_pcall_ptr == NULL) return 1; L = luaL_newstate_ptr(); luaL_openlibs_ptr(L); /* Cannot use macro luaL_dostring, because lua_pcall is renamed ! */ luaL_loadstring_ptr(L, "print 'Hello World'") || lua_pcall_ptr(L, 0, LUA_MULTRET, 0); lua_close_ptr(L); return 0; }
lua.h, lauxlib.h and lualib.h
內部的函數原型;luaL_dostring
這類的巨集,因為它們是使用其他 API 函數來實作的,會在原始程式碼中重新命名;__cdecl
。示範的第三個版本,使用提議的簡化方式。你必須將它與另一個 lua_dyn.c
檔案一起編譯:它可以是 gcc -o hello hello.c lua_dyn.c
#include <windows.h> #include "lua_dyn.h" #define LUA_PREFIX LuaFunctions. lua_All_functions LuaFunctions; int main(int argc, char* argv[]) { lua_State* L; HMODULE module = LoadLibrary("lua5.1.dll"); if(!luaL_loadfunctions(module, &LuaFunctions, sizeof(LuaFunctions))) return 1; L = lua_open(); luaL_openlibs(L); luaL_dostring(L, "print 'Hello World'"); lua_close(L); return 0; }
lua_dyn.c
呼叫一個外部函數 luaL_loadfunctions
。接著,可以使用與在自動載入中相同的程式碼,包括所有巨集!
export_h.lua
用於從 Lua 5.1 標頭檔 lua.h
、lauxlib.h
和 lualib.h
產生檔案 lua_dyn.h
和 lua_dyn.c
。它可以在任何標準的 Lua 5.1 直譯器上執行。每次指令碼看到外部函數定義時,它就會用一個 typedef
、函數結構中的一個欄位,以及一個 #define
來取代它。範例LUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud); LUA_API void (lua_close) (lua_State *L); LUA_API lua_State *(lua_newthread) (lua_State *L);
typedef lua_State * (__cdecl *lua_newstate_t) (lua_Alloc f, void *ud); typedef void (__cdecl *lua_close_t) (lua_State *L); typedef lua_State * (__cdecl *lua_newthread_t) (lua_State *L); ... typedef struct lua_All_functions { lua_newstate_t Newstate; lua_close_t Close; lua_newthread_t Newthread; ... } lua_All_functions; ... #define lua_newstate LUA_PREFIX Newstate #define lua_close LUA_PREFIX Close #define lua_newthread LUA_PREFIX Newthread
lua_All_functions
包含指向所有外部 Lua API 函數的指標。這允許在單一行中實例化所有函數指標:宣告靜態變數,或者使用 malloc 或使用 C++ 中的新增為它分配記憶體。它也簡化了初始化:單一函數呼叫至 luaL_loadfunctions
將會從函數名稱表匯出所有函數。巨集定義讓你能夠使用標準函數名稱,例如 lua_newstate
,而不是結構存取,例如 luaFct.Newstate
或 m_pLuaF->Newstate
。此外,它確保相容於建構在其他函數上的 API 巨集。為了編譯這個,你必須將 LUA_PREFIX
定義為 luaFct.
或 m_pLuaF->
。由於原始函數宣告已從標頭中移除,因此不會發生命名衝突。範例:在前一個範例中,第 16 行
luaL_dostring(L, "print 'Hello World'");
(LuaFunctions. LoadstringL(L, "print 'Hello World'") || LuaFunctions. Pcall(L, 0, (-1), 0));
LuaFunctions
是 struct lua_All_functions
的靜態宣告。
lua_dyn.c
與 lua_dyn.h
使用 Lua 5.1.2 標頭檔建置,且未經修改。出於這個原因,它可能適用於任何 Lua 解譯器的標準套件。已在 Windows 和 Linux 平臺上進行測試。如果您有不同的組態,請採取下列操作export_h.lua
。前 16 行是組態檔。您可能想要新增其他包含檔(給您的自訂函式庫),或調整 conf_defines
表格以反映 luaconf.h
中的參數;cd lua-5.1.2/src
);export_h.lua
(lua export_h.lua
);lua_dyn.*
移至您想要的任何位置。如果您能重新編譯 Lua 共享函式庫,則您可能偏好於在該函式庫內編譯檔案 lua_dyn.c
,這樣可以進一步簡化手動載入。若是如此,請將 loaderfct_indll
標記變更為 true
,這會定義一個說明巨集 LUA_LOAD_FUNCTIONS
。該巨集會從 Lua SL 手動載入函式 luaL_loadfunctions
,然後在單一指令中呼叫它。
-- PatrickRapin