C++11 在单个Lua状态下使用多个脚本并使用_ENV

C++11 在单个Lua状态下使用多个脚本并使用_ENV,c++11,lua,lua-api,lua-c++-connection,C++11,Lua,Lua Api,Lua C++ Connection,我目前正在学习如何使用Lua C API,虽然我已经成功地在C/C++和Lua之间绑定函数,但我有几个问题: 将多个脚本加载到一个lua\u状态,这是一个好主意吗?有没有关闭特定区块的方法?如果脚本不再使用,如何在保留所有其他内容的同时将其从lua\u状态中清除 使用可能对函数/全局变量使用相同名称的脚本的最佳方式是什么?如果我加载所有的定义,新的定义将覆盖旧的定义 在线阅读后,我想我需要分离每个加载的块 进入不同的环境。我设想这项工作的方式是 加载块时,我会为其指定一个唯一的环境名称 需要使用

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

  • 将多个脚本加载到一个
    lua\u状态
    ,这是一个好主意吗?有没有关闭特定区块的方法?如果脚本不再使用,如何在保留所有其他内容的同时将其从
    lua\u状态中清除

  • 使用可能对函数/全局变量使用相同名称的脚本的最佳方式是什么?如果我加载所有的定义,新的定义将覆盖旧的定义

    在线阅读后,我想我需要分离每个加载的块 进入不同的环境。我设想这项工作的方式是 加载块时,我会为其指定一个唯一的环境名称 需要使用它我只是用这个名字来取 从LUA\U注册表索引中选择环境,然后执行该操作。所以 到目前为止,我还不知道怎么做。网上有一些例子 但是他们使用Lua5.1

  • 将多个脚本加载到一个lua_状态是一个好主意吗

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

    有没有关闭特定区块的方法

    Chunk只是一个“function”类型的值。当您没有将该值存储在任何位置时,区块将被垃圾收集。
    区块生成的任何内容(全局或在外部某处有引用的局部变量)将继续存在

    如何在保留所有其他内容的同时将其从lua_状态中清除

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

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

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

    我设想这项工作的方式是,每次加载块时,我都为其指定一个唯一的环境名称


    如果脚本来自外部(由用户编写或从其他外部源接收),则应该这样做。沙箱很酷,但如果块是你的内部材料,就没有必要使用沙箱。考虑改写无全局代码的代码。如果区块生成其他对象,则返回一些对象(api表或闭包)——您可以多次调用该区块而无需重新加载它。或者保存一个全局模块接口,如果chunk表示类似Lua的模块。如果您没有很好地组织代码,那么您将被迫使用单独的环境,并且您必须为每个脚本准备新的环境,复制基本内容,如print/pairs/string/等。在运行时,您将有许多中断,直到您发现新环境中缺少了更多内容,诸如此类。

    在多翻了几遍之后,我找到了我认为是我一直在寻找的解决方案。我不确定这是否是正确/最好的方法,但它在我的基本测试用例中起作用@雅各布对这个问题的回答帮助很大

    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


    这个基本测试用例可以根据需要工作。我将继续测试,看看是否出现任何问题/改进。如果有人认为我可以改进这段代码,或者发现一些明显的错误,请留下评论。

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

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


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

    谢谢您的回答!我想我应该在我最初的帖子中提到,我的最终目标是沙箱,或者至少运行不是我写的脚本。我会有必须遵循的一般准则,但我也希望用户和贡献者拥有自由。这些准则可能要求用户在其所有脚本中都有一个全局定义的表a。这意味着我需要使用环境,因为它们在任何时候都可能有任意数量的脚本处于该状态,但我不知道如何从C/C++中执行,这不是我在下面的回答中所做的吗?_ENV不是一个表吗?我真的很喜欢这个答案,并且正在使用类似的东西。如何在同一文件中的函数之间共享内存?理想情况下,我希望在函数之外的文件中创建变量,然后为文件中的函数提供可修改的访问权限;到目前为止,它们有访问权限,但是,它们的修改不会持续超过函数本身。是否可以在文件中声明变量?在我的示例中,变量x对于所有函数都是全局的,更改应该保持不变。你能给我举个例子吗?当然。“本地y=0;函数First();y=12;z=21;end;函数Second();print(y)-12;print(z)-nil;end”对不起,注释中没有换行符,对吗?所以,我有两个函数,我有一个“文件级”变量Y,可以被函数访问(闭包,对吗?),然后我在第一个函数中创建一个变量,但是调用第二个,我没有看到变量。第二个()在同一个文件中吗?是的,它们都在同一个文件中。其思想是将file1视为一个“类”,其中在函数之外定义的任何内容都应该是一个成员变量(我认为在您创建的_ENV中,是吗?),然后函数就是成员函数。因此,在C++中,我将调用整个文件,使之具有