2

假设我有两个 Lua 文件,我将从标准 Lua C API 使用它们,它们共享一个公共库:

common.lua

function printHello(name)
    print("Hello from " .. name)
end

文件1.lua

require "common"
local scriptName = "file1"

function doSomething()
    printHello(scriptName)
end

文件2.lua

require "common"
local scriptName = "file2"

function doSomething()
    printHello(scriptName)
end

现在说我想让两个 file*.lua 文件共享相同 lua_State的. 在不更改任何 Lua 代码的情况下,如何以可以调用特定代码的方式加载文件doSomething()

有没有一种方法可以将加载的文件(函数、变量、表)中的“所有内容”移动到lua_State使用脚本名称(或其他名称)作为键的全局表中?另外,有没有办法让file1.lua和file2.lua可以共享common.lua的“内存”版本?

我正在使用 Lua 5.1。

谢谢!

4

2 回答 2

1

以下是在纯 Lua 5.1 中的操作方式:

file1_env = setmetatable({}, {__index = _G})
local file1_chunk = loadfile('file1.lua')
setfenv(file1_chunk, file1_env)
file1_chunk()

file2_env = setmetatable({}, {__index = _G})
local file2_chunk = loadfile('file2.lua')
setfenv(file2_chunk, file2_env)
file2_chunk()

file1_env.doSomething() -- prints "Hello from file1"
file2_env.doSomething() -- prints "Hello from file2"

发生的事情是您正在更改每个文件块运行的环境,因此不是将它们的doSomething功能放在全局环境中从而相互踩踏,而是它们进入自己的本地环境(使用元表以便他们可以使用东西在全球环境中,如requireprint)。并且根据要求,common.lua只需要运行一次,你会看到你是否printHello('common')在它的末尾放了类似的东西。

如果您想从 C 中执行此操作,我使用的所有功能都可以直接转换为 C API,如下所示:

#include <lua5.1/lua.h>
#include <lua5.1/lualib.h>
#include <lua5.1/lauxlib.h>

int main(void) {
    lua_State *L = luaL_newstate();
    luaL_openlibs(L);

    /* stack is empty */
    lua_createtable(L, 0, 1);
    /* -1: file1_env */
    lua_createtable(L, 0, 1);
    /* -2: file1_env, -1: file1_env_mt */
    lua_pushvalue(L, LUA_GLOBALSINDEX);
    /* -3: file1_env, -2: file1_env_mt, -1: _G */
    lua_setfield(L, -2, "__index");
    /* -2: file1_env, -1: file1_env_mt */
    lua_setmetatable(L, -2);
    /* -1: file1_env */
    luaL_loadfile(L, "file1.lua");
    /* -2: file1_env, -1: file1_chunk */
    lua_pushvalue(L, -2);
    /* -3: file1_env, -2: file1_chunk, -1: file1_env */
    lua_setfenv(L, -2);
    /* -2: file1_env, -1: file1_chunk */
    lua_call(L, 0, 0);
    /* -1: file1_env */
    lua_setglobal(L, "file1_env");
    /* stack is empty */
    lua_createtable(L, 0, 1);
    /* -1: file2_env */
    lua_createtable(L, 0, 1);
    /* -2: file2_env, -1: file2_env_mt */
    lua_pushvalue(L, LUA_GLOBALSINDEX);
    /* -3: file2_env, -2: file2_env_mt, -1: _G */
    lua_setfield(L, -2, "__index");
    /* -2: file2_env, -1: file2_env_mt */
    lua_setmetatable(L, -2);
    /* -1: file2_env */
    luaL_loadfile(L, "file2.lua");
    /* -2: file2_env, -1: file2_chunk */
    lua_pushvalue(L, -2);
    /* -3: file2_env, -2: file2_chunk, -1: file2_env */
    lua_setfenv(L, -2);
    /* -2: file2_env, -1: file2_chunk */
    lua_call(L, 0, 0);
    /* -1: file2_env */
    lua_setglobal(L, "file2_env");
    /* stack is empty */
    lua_getglobal(L, "file1_env");
    /* -1: file1_env */
    lua_getfield(L, -1, "doSomething");
    /* -2: file1_env, -1: file1_env.doSomething */
    lua_call(L, 0, 0);
    /* -1: file1_env */
    lua_pop(L, 1);
    /* stack is empty */
    lua_getglobal(L, "file2_env");
    /* -1: file2_env */
    lua_getfield(L, -1, "doSomething");
    /* -2: file2_env, -1: file2_env.doSomething */
    lua_call(L, 0, 0);
    /* -1: file2_env */
    lua_pop(L, 1);
    /* stack is empty */

    lua_close(L);
    return 0;
}
于 2020-05-08T17:10:54.650 回答
0

仅供参考,上述答案不再适用于最新版本的 LUA。具体来说,

lua_pushvalue(L, LUA_GLOBALSINDEX);

lua_setfenv(L, -2);

不再支持。因为我是 LUA 新手,所以我不知道应该使用什么等效的 LUA api 调用作为替代。如果有人可以更新该答案以使用 LUA 5.4,将不胜感激。我很抱歉我没有遵守协议,没有直接在答案中发表评论,但我显然没有足够的声誉这样做。

于 2022-03-04T15:06:08.783 回答