轻松手动加载库 |
|
本文介绍了一种方法,只需更改程序源代码中 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 和 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++ 中的 new 为其分配内存。它还简化了初始化:对 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