单个 Lua 状态下的多个脚本并使用 _ENV

Multiple scripts in a single Lua state and working with _ENV

我目前正在学习如何使用 Lua C API,虽然我在 C/C++ 和 Lua 之间成功绑定了函数,但我有几个问题:

  1. 将多个脚本加载到一个 lua_State 中是个好主意吗?有没有办法关闭特定的块?如果脚本不再使用,我如何从 lua_State 中清除它同时保留其他所有内容?

  2. 使用可能对 functions/global 变量使用相同名称的脚本的最佳方式是什么?如果我加载所有这些,较新的定义会覆盖较旧的定义。

    在线阅读后,我认为我需要将每个加载的块分开 进入不同的环境。我设想这项工作的方式是 加载块时,我为它分配一个唯一的环境名称,当我 需要使用它我只是用那个名字来获取 来自 LUA_REGISTRYINDEX 的环境并执行操作。所以 到目前为止,我还没有想出如何做到这一点。网上有例子 但他们使用 Lua 5.1.

Is it a good idea to load multiple scripts into a single lua_State?

是的,绝对是。除非这些脚本不相关并且应该 运行 在多个并行线程中。

Is there a way to close specific chunks?

Chunk 只是一个 "function" 类型的值。当您没有将该值存储在任何地方时 - 块将被垃圾收集。
生成的任何块 - 全局变量或在外部某处有引用的本地变量 - 那些将继续存在。

how to clear it from the lua_State while retaining everything else?

这取决于您如何看待该块。它只是一组功能,还是代表一些具有自己状态的实体。如果您不创建全局函数和变量,那么在单独的脚本文件中定义的所有内容都将是本地块,并且在没有剩余块引用时将被删除。

What is the best way use scripts that may use the same name for functions/global variables?

考虑重写您的代码。不要创建任何全局变量,除非明确要求它与程序的其他部分建立通信。使变量成为本地变量(由 chunk 拥有),或将其存储在 table/closure 中,它将由 chunk 作为新对象返回 - chunk 可能是一个生产新对象的工厂,而不只是 脚本 .
另外 Lua 运行 使用局部变量会更快。

The way I envision this working is each time a chunk is loaded I assign it a unique environment name

如果脚本来自外部 - 由用户编写,或从其他外部来源接收,您应该这样做。沙盒很酷,但如果块是您的内部内容,则不需要沙盒。考虑在没有全局变量的情况下重写代码。 Return 一些对象(api table,或闭包)如果你的块产生其他对象 - 你可以多次调用该块而无需重新加载它。或者保存一个全局模块接口,如果 chunk 表示 Lua-like 模块。如果你没有很好地组织你的代码,那么你将被迫使用单独的环境,并且你必须为每个脚本准备新的环境,复制print/pairs/string/etc之类的基本内容。您将在 运行 时间内有很多休息时间,直到您弄清楚新环境还缺少什么,等等。

在仔细研究之后,我发现了我认为是我正在寻找的解决方案。我不确定这是否是 correct/best 的方法,但它适用于我的基本测试用例。 @jpjacobs 对 this 问题的回答很有帮助。

test1.lua

x = 1
function hi()
    print("hi1");
    print(x);
end
hi()

test2.lua

x =2
function hi()
    print("hi2");
    print(x);
end
hi()

main.cpp

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

    char* file1 = "Rooms/test1.lua";
    char* file2 = "Rooms/test2.lua";

    //We load the file
    luaL_loadfile(L, file1);
    //Create _ENV tables
    lua_newtable(L);
    //Create metatable
    lua_newtable(L);
    //Get the global table
    lua_getglobal(L, "_G");
    lua_setfield(L, -2, "__index");
    //Set global as the metatable
    lua_setmetatable(L, -2);
    //Push to registry with a unique name.
    //I feel like these 2 steps could be merged or replaced but I'm not sure how
    lua_setfield(L, LUA_REGISTRYINDEX, "test1");
    //Retrieve it. 
    lua_getfield(L, LUA_REGISTRYINDEX, "test1");
    //Set the upvalue (_ENV)
    lua_setupvalue(L, 1, 1);
    //Run chunks
    lua_pcall(L, 0, LUA_MULTRET, 0);

    //Repeat
    luaL_loadfile(L, file2);
    lua_newtable(L);
    lua_newtable(L);
    lua_getglobal(L, "_G");
    lua_setfield(L, -2, "__index");
    lua_setmetatable(L, -2);
    lua_setfield(L, LUA_REGISTRYINDEX, "test2");
    lua_getfield(L, LUA_REGISTRYINDEX, "test2");
    lua_setupvalue(L, 1, 1);
    lua_pcall(L, 0, LUA_MULTRET, 0);

    //Retrieve the table containing the functions of the chunk
    lua_getfield(L, LUA_REGISTRYINDEX, "test1");
    //Get the function we want to call
    lua_getfield(L, -1, "hi");
    //Call it
    lua_call(L, 0, 0);
    //Repeat
    lua_getfield(L, LUA_REGISTRYINDEX, "test2");
    lua_getfield(L, -1, "hi");
    lua_call(L, 0, 0);
    lua_getfield(L, LUA_REGISTRYINDEX, "test2");
    lua_getfield(L, -1, "hi");
    lua_call(L, 0, 0);
    lua_getfield(L, LUA_REGISTRYINDEX, "test1");
    lua_getfield(L, -1, "hi");
    lua_call(L, 0, 0);

    lua_close(L);
}

输出:

hi1
1
hi2
2
hi1
1
hi2
2
hi2
2
hi1
1

我正在使用 Lua 5.3.2 和 Visual Studio 2013 如果这意味着什么。

此基本测试用例可按需运行。我会继续测试,看看是否有任何 issues/improvements 出现。如果有人看到我可以改进此代码或发现明显的错误,请发表评论。

您应该将每个脚本视为不同的模块。 就像您的代码中有超过 1 个 "require"。

您的 'loaded chunk' 应该 return table 将全局存储。

加载大量全局变量不是个好主意,这可能会在您添加更多模块后造成不良后果。