轻松手动加载库 |
|
本文介绍了一种方法,只需更改程序源代码中 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