单个 Lua 状态下的多个脚本并使用 _ENV
Multiple scripts in a single Lua state and working with _ENV
我目前正在学习如何使用 Lua C API,虽然我在 C/C++ 和 Lua 之间成功绑定了函数,但我有几个问题:
将多个脚本加载到一个 lua_State
中是个好主意吗?有没有办法关闭特定的块?如果脚本不再使用,我如何从 lua_State
中清除它同时保留其他所有内容?
使用可能对 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 将全局存储。
加载大量全局变量不是个好主意,这可能会在您添加更多模块后造成不良后果。
我目前正在学习如何使用 Lua C API,虽然我在 C/C++ 和 Lua 之间成功绑定了函数,但我有几个问题:
将多个脚本加载到一个
lua_State
中是个好主意吗?有没有办法关闭特定的块?如果脚本不再使用,我如何从lua_State
中清除它同时保留其他所有内容?使用可能对 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 将全局存储。
加载大量全局变量不是个好主意,这可能会在您添加更多模块后造成不良后果。