5

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

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

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

    在线阅读后,我认为我需要将每个加载的块分开到不同的环境中。我设想这种工作的方式是,每次加载一个块时,我都会为其分配一个唯一的环境名称,当我需要使用它时,我只需使用该名称从中获取环境LUA_REGISTRYINDEX并执行操作。到目前为止,我还没有弄清楚如何做到这一点。网上有例子,但他们使用 Lua 5.1。

4

3 回答 3

2

将多个脚本加载到单个 lua_State 中是个好主意吗?

当然是。除非这些脚本不相关并且应该在多个并行线程中运行。

有没有办法关闭特定的块?

块只是“函数”类型的值。当您没有将该值存储在任何地方时 - 块将被垃圾收集。
任何产生的块——全局变量,或在外部有引用的局部变量——都将继续存在。

如何在保留其他所有内容的同时从 lua_State 中清除它?

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

使用可能对函数/全局变量使用相同名称的脚本的最佳方法是什么?

考虑重写你的代码。不要创建任何全局变量,除非明确要求与程序的其他部分建立通信。将变量设置为本地变量(由块拥有),或将其存储在表/闭包中,块将作为新对象返回 - 块可能是生产新对象的工厂,而不是脚本
此外,Lua 使用局部变量运行得更快。

我设想这种工作的方式是每次加载一个块时,我都会为其分配一个唯一的环境名称

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

于 2016-04-03T09:01:10.483 回答
2

在四处寻找更多之后,我发现我认为是我正在寻找的解决方案。我不确定这是否是正确/最佳的方法,但它适用于我的基本测试用例。@jpjacobs 对这个问题的回答很有帮助。

测试1.lua

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

测试2.lua

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

主文件

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 一起使用。

这个基本的测试用例根据需要工作。我将继续测试以查看是否出现任何问题/改进。如果有人看到我可以改进此代码或明显错误的任何方法,请发表评论。

于 2016-04-04T17:12:33.913 回答
0

您应该将每个脚本视为不同的模块。就像您的代码中有超过 1 个“要求”一样。

您的“已加载块”应返回将全局存储的表。

加载大量全局变量并不是一个好主意,这可能会在您添加更多模块后导致坏事。

于 2016-04-04T22:33:50.450 回答