如何关闭一段Lua代码

我想知道是否有可能仅在使用 luaL_dostring 加载的一段Lua代码后,关闭该代码块,以便内部所有变量都可以相应地释放。

以下是我的简单示例:

lua_State *L = luaL_newstate();
luaL_openlibs(L);
luaL_dostring(L, "a = 2"); // Script A
// 如何在此处关闭脚本A?
luaL_dostring(L, "print(a)"); // Script B
lua_close(L);

当我运行此代码时,它将打印出 2,但我想知道是否可能以某种方式关闭(或释放)仅从内存中加载的首个代码块,以便打印出 nil

点赞
用户1944004
用户1944004

大致上,您想要将脚本A与脚本B使用不同的全局环境(sandbox)运行。这可以通过将全局表备份到注册表中并用空表替换它来轻松完成(可以选择在sandbox内部填充所需内容的空表)。完成脚本A后,只需从注册表检索旧的全局表并将其恢复为当前的全局表即可。

除此之外,我建议将全局变量的使用降至最低。事实上,在编写Lua代码时我从不使用任何全局变量。通常我会在本地表格中记录信息并将其传递。这可能是一种更函数式的Lua编写风格。

#include <iostream>
#include <lua.hpp>

int main() {
    lua_State *L = luaL_newstate();
    luaL_openlibs(L);

    // START SANDBOX

    // Push the global table into the registry
    lua_pushglobaltable(L);
    lua_setfield(L, LUA_REGISTRYINDEX, "globals");

    // Push a new empty table and make it the global table
    lua_newtable(L);
    lua_rawseti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS);

    // Script A
    if (luaL_dostring(L, "a = 2") != 0) {
        std::cerr << "lua:" << lua_tostring(L, -1) << '\n';
        lua_pop(L, 1);
    }

    // Retrieve the global table from the registry and make it the global table
    lua_getfield(L, LUA_REGISTRYINDEX, "globals");
    lua_rawseti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS);

    // Optional: Remove the global table from the registry
    lua_pushnil(L);
    lua_setfield(L, LUA_REGISTRYINDEX, "globals");

    // END SANDBOX

    // Script B
    if (luaL_dostring(L, "print(a)") != 0) {
        std::cerr << "lua:" << lua_tostring(L, -1) << '\n';
        lua_pop(L, 1);
    }
    lua_close(L);
}
$ clang++ -Wall -Wextra -Wpedantic -I /usr/include/lua5.2/ test.cpp -llua5.2
$ ./a.out
nil
2018-07-18 08:40:50