轻松手动加载库 |
|
本文介绍了一种方法,只需更改程序源代码中的 5 行 C 代码,即可轻松地将 Lua 5.1 中的所有函数手动加载到您的应用程序中!如果您已经手动加载了一些共享库(.dll 或 .so),则可以跳过接下来的四个介绍章节。源代码可以在这里找到:[lua_dyn.zip]
*.dll)。在 Unix 上,通常称为共享对象(*.so)或共享库(*.sa)。两者都指的是相同性质的可执行文件,我们将从现在开始称之为 SL(共享库)。SL 包含编译和链接好的机器代码,向应用程序导出公共函数,但自身没有 main() 函数,因此不能直接执行。相反,SL 由操作系统加载到应用程序的内存空间中,然后应用程序就可以像 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