4

考虑以下使用 Lua C API 的 C++ 代码:

#include <string>
#include <cassert>

#include <lua/lua.hpp>

class AwesomeThing
{
    lua_State* _lua;
    std::string _name;

public:
    AwesomeThing(lua_State* L, const std::string& name, const std::string& luafile)
        : _lua{ L },
          _name{ name }
    {
        assert(luaL_loadfile(_lua, luafile.c_str()) == 0); // 1:chunk

        lua_newtable(_lua); // 1:chunk, 2:tbl
        lua_newtable(_lua); // 1:chunk, 2:tbl, 3:tbl(mt)
        lua_getglobal(_lua, "_G"); // 1:chunk, 2: tbl, 3:tbl(mt), 4:_G
        lua_setfield(_lua, 3, "__index"); // 1:chunk, 2: tbl, 3:tbl(mt)
        lua_setmetatable(_lua, 2); // 1:chunk, 2: tbl

        lua_setupvalue(_lua, -2, 1); // 1:chunk
        if (lua_pcall(_lua, 0, 0, 0) != 0) // compiled chunk
        {
            auto error = lua_tostring(_lua, -1);
            throw std::runtime_error(error);
        }

        lua_setglobal(_lua, _name.c_str()); // empty stack
    }

    void init()
    {
        lua_getglobal(_lua, _name.c_str()); // 1:env
        assert(lua_isnil(_lua, 1) == 0);

        lua_getfield(_lua, 1, "onInit"); // 1:env, 2:func
        assert(lua_isnil(_lua, 2) == 0);
        assert(lua_isfunction(_lua, 2) == 1);

        assert(lua_pcall(_lua, 0, LUA_MULTRET, 0) == 0); // 1:env, 2:retval

        lua_pop(_lua, 1); // -1:env
        lua_pop(_lua, 1); // empty stack
        assert(lua_gettop(_lua) == 0);
    }
};

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

    AwesomeThing at1(L, "thing1", "file1.lua");
    AwesomeThing at2(L, "thing2", "file2.lua");

    at1.init();
    at2.init();

    return 0;
}

有两个非常基本的 Lua 文件:

文件1.lua

function onInit()
    print("init file1")
end

文件2.lua

function onInit()
    print("init file2")
end

at2照原样,我在的构造函数调用中遇到错误lua_pcall尝试调用表值

当我注释掉所有对 的引用/调用时,我在's atat2中得到一个错误:PANIC: unprotected error in call to Lua API (attempt to index a nil value)at1init()lua_getfield(_lua, 1, "onInit")

我觉得我在处理沙盒的方式中缺少一些基本的东西。我已尽我最大的努力遵循我在网上找到的其他一些 Lua 5.2 沙盒示例,但到目前为止没有任何帮助。

4

1 回答 1

3

在自己弄乱了代码之后,我能够修复它,错误似乎只是来自一些错误。

  • lua_pcall从堆栈中弹出被调用的函数,但在您的代码中的这两种情况下,您都假设该函数在lua_pcall. 这会导致糟糕的堆栈操作。
  • 在构造函数中,您显然尝试存储对块(函数)而不是环境表的引用。这甚至不起作用,因为该函数已经弹出。如果它确实有效,则lua_getfield调用init()将无法按预期工作,因为该块没有名为的字段onInit——环境表有。

修复构造函数涉及创建环境表并以相反的顺序加载块,以便在函数调用后将环境表留在堆栈上:

        lua_newtable(_lua); // 1:tbl

        assert(luaL_loadfile(_lua, luafile.c_str()) == 0); // 1:tbl, 2:chunk

        lua_newtable(_lua); // 1:tbl, 2:chunk, 3:tbl(mt)
        lua_getglobal(_lua, "_G"); // 1:tbl, 2:chunk, 3:tbl(mt), 4:_G
        lua_setfield(_lua, 3, "__index"); // 1:tbl, 2:chunk, 3:tbl(mt)
        lua_setmetatable(_lua, 1); // 1:tbl, 2:chunk
        lua_pushvalue(_lua, 1); // 1:tbl, 2:chunk, 3:tbl

        lua_setupvalue(_lua, -2, 1); // 1:tbl, 2:chunk
        if (lua_pcall(_lua, 0, 0, 0) != 0) // compiled chunk
        {
            auto error = lua_tostring(_lua, -1);
            throw std::runtime_error(error);
        }

        // 1:tbl

        lua_setglobal(_lua, _name.c_str()); // empty stack

然后init(),由于您使用LUA_MULTRET,只需将两个 pop 调用替换为 . 即可清除堆栈lua_settop(_lua, 0)

于 2020-08-29T14:58:52.510 回答