Lua C API - 加载具有相同名称的变量和函数的多个文件

Lua C API - loading multiple files with variables and functions of the same names

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

common.lua

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

file1.lua

require "common"
local scriptName = "file1"

function doSomething()
    printHello(scriptName)
end

file2.lua

require "common"
local scriptName = "file2"

function doSomething()
    printHello(scriptName)
end

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

有没有一种方法可以将 "everything" 从加载的文件(函数、变量、tables)移动到 lua_State 中的全局 table 使用脚本名称(或其他)作为关键?另外,有没有办法让 file1.lua 和 file2.lua 可以共享 common.lua 的 "in memory" 版本?

我正在使用 Lua 5.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;
}

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

lua_pushvalue(L, LUA_GLOBALSINDEX);

lua_setfenv(L, -2);

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