简体   繁体   English

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

[英]Multiple scripts in a single Lua state and working with _ENV

I'm currently learning how to use the Lua C API and while I've had success binding functions between C/C++ and Lua, I have a few questions: 我目前正在学习如何使用Lua C API,虽然我在C / C ++和Lua之间有成功的绑定功能,但我有几个问题:

  1. Is it a good idea to load multiple scripts into a single lua_State ? 将多个脚本加载到单个lua_State是一个好主意吗? Is there a way to close specific chunks? 有没有办法关闭特定的块? If a script is no longer in use how can I clear it from the lua_State while retaining everything else? 如果脚本不再使用,如何在保留其他所有内容的情况下从lua_State清除它?

  2. What is the best way use scripts that may use the same name for functions/global variables? 使用可能对函数/全局变量使用相同名称的脚本的最佳方法是什么? If I load all of them the newer definitions override the older ones. 如果我加载所有这些,则较新的定义会覆盖旧的定义。

    After reading online I think I need to separate each loaded chunk into different environments. 在线阅读后,我想我需要将每个加载的块分成不同的环境。 The way I envision this working is each time a chunk is loaded I assign it a unique environment name, when I need to work with it it I just use that name to fetch the environment from the LUA_REGISTRYINDEX and perform the operation. 我设想这个工作的方式是每次加载一个块我为它分配一个唯一的环境名称,当我需要使用它时,我只使用该名称从LUA_REGISTRYINDEX获取环境并执行操作。 So far I haven't figured out how to do this. 到目前为止,我还没有想出如何做到这一点。 There are examples online but they use Lua 5.1. 网上有例子,但他们使用的是Lua 5.1。

After poking around some more I found what I think is the solution I was looking for. 在探索了一些后,我发现我认为是我正在寻找的解决方案。 I'm not sure if this is the correct/best way to do it, but it works in my basic test case. 我不确定这是否是正确/最好的方法,但它适用于我的基本测试用例。 @jpjacobs answer on this question helped a lot. @jpjacobs对这个问题的回答帮了很多忙。

test1.lua test1.lua

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

test2.lua test2.lua

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

main.cpp 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);
}

Output: 输出:

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

I'm using Lua 5.3.2 with Visual Studio 2013 if that means anything. 如果这意味着什么,我在Visual Studio 2013中使用Lua 5.3.2。

This basic test case works as needed. 此基本测试用例可根据需要运行。 I'll continue testing to see if any issues/improvements come up. 我会继续测试,看看是否有任何问题/改进。 If any sees any way I could improve this code or and glaring mistakes, please leave a comment. 如果有任何方式我可以改进此代码或明显错误,请发表评论。

Is it a good idea to load multiple scripts into a single lua_State? 将多个脚本加载到单个lua_State中是一个好主意吗?

Yes, definitely. 当然是。 Unless those scripts is unrelated and should run in multiple parallel threads. 除非这些脚本不相关并且应该在多个并行线程中运行。

Is there a way to close specific chunks? 有没有办法关闭特定的块?

Chunk is just a value of type "function". 块只是“函数”类型的值。 When you don't have that value stored anywhere - chunk will be garbage-collected. 当你没有将值存储在任何地方时 - chunk将被垃圾收集。
Anything chunk produced - globals, or locals that has references somewhere outside - those will live on. 任何产生的块 - 全局变量,或者在外面有引用的本地人 - 都将继续存在。

how to clear it from the lua_State while retaining everything else? 如何在保留其他所有内容的同时从lua_State中清除它?

That depends on how do you see that chunk. 这取决于你如何看待那块。 Is it just a set of functions, or represents some entity with own state. 它只是一组函数,还是代表某个具有自己状态的实体。 If you don't create global functions and variables, then everything defined in separate script file will be local to chunk, and will be removed when there's no references to chunk left. 如果您不创建全局函数和变量,那么在单独的脚本文件中定义的所有内容都将是块的本地,并且在没有对块的引用时将被删除。

What is the best way use scripts that may use the same name for functions/global variables? 使用可能对函数/全局变量使用相同名称的脚本的最佳方法是什么?

Consider rewriting your code. 考虑重写代码。 Do not create any globals, unless it's explicitly required to build communications with other parts of your program. 不要创建任何全局变量,除非明确要求与程序的其他部分建立通信。 Make variables local (owned by chunk), or store it in table/closure that will be returned by chunk as new object - chunk might be a factory producing new objects, and not jush the script . 使变量本地(由chunk拥有),或将其存储在将由chunk作为新对象返回的表/闭包中 - chunk可能是生成新对象的工厂,而不是jush 脚本
Also Lua runs just faster with local variables. Lua也使用局部变量运行得更快。

The way I envision this working is each time a chunk is loaded I assign it a unique environment name 我设想这个工作的方式是每次加载一个块我给它分配一个唯一的环境名称

You should do that if scripts comes from outside - written by users, or received from other external sources. 如果脚本来自外部 - 由用户编写或从其他外部源接收,则应该这样做。 Sandboxing is cool, but there's no need in sandboxing if chunks is your internal stuff. 沙盒很酷,但是如果块是你的内部东西,则不需要沙盒。 Consider rewriting code without globals. 考虑重写没有全局变量的代码。 Return some object (api table, or closure) if your chunk produces other objects - you can call that chunk many times without reloading it. 如果你的块产生其他对象,则返回一些对象(api表或闭包) - 你可以多次调用该块而无需重新加载它。 Or save one global - module interface, if chunk represents Lua-like module. 或者保存一个全局模块接口,如果chunk代表Lua-like模块。 If you don't organize your code well, then you will be forced to use separate environments, and you'll have to prepare new environment for every script, copy basic stuff like print/pairs/string/etc. 如果你没有很好地组织你的代码,那么你将被迫使用单独的环境,你必须为每个脚本准备新的环境,复制print / pairs / string /等基本内容。 You'll have many breaks in run time until you figure out what's more is missing from new environment, and so on. 在运行时间你会有很多休息时间,直到你弄清楚新环境中缺少什么,等等。

you should treat each of your scripts as different module. 您应该将每个脚本视为不同的模块。 just like you have more then 1 "require" in your code. 就像你的代码中有超过1“需要”一样。

your 'loaded chunk' should return table that will be stored globally. 你的'加载的块'应该返回将全局存储的表。

this isn't good idea to load a lot of global variables, this can cause bad thing after you will add more modules. 加载很多全局变量并不是一个好主意,这会在你添加更多模块后造成坏处。

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

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