繁体   English   中英

单个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. 使用可能对函数/全局变量使用相同名称的脚本的最佳方法是什么? 如果我加载所有这些,则较新的定义会覆盖旧的定义。

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

在探索了一些后,我发现我认为是我正在寻找的解决方案。 我不确定这是否是正确/最好的方法,但它适用于我的基本测试用例。 @jpjacobs对这个问题的回答帮了很多忙。

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

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

此基本测试用例可根据需要运行。 我会继续测试,看看是否有任何问题/改进。 如果有任何方式我可以改进此代码或明显错误,请发表评论。

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

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

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

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

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

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

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

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

我设想这个工作的方式是每次加载一个块我给它分配一个唯一的环境名称

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

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

你的'加载的块'应该返回将全局存储的表。

加载很多全局变量并不是一个好主意,这会在你添加更多模块后造成坏处。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM