考虑以下使用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文件:
file1.lua
function onInit()
print("init file1")
end
file2.lua
function onInit()
print("init file2")
end
目前存在问题的是at2
的构造函数在lua_pcall
处报错:尝试调用表值
当我将所有对at2
的引用/调用都注释掉时,at1
中的init()
在lua_getfield(_lua, 1, "onInit")
处出现错误:PANIC:在调用Lua API时未受保护的错误(尝试索引空值)
我感觉我在处理沙箱环境方面缺少了一些基本的东西。我已经尽力遵循我在网上找到的一些Lua 5.2沙盒示例,但迄今为止没有什么帮助。
luaL_dostring
弄清楚了如何做到这一点,但我仍然不确定我的C代码哪里出了问题,但这解释了一切。非常感谢! - Addylua_type()
函数非常有用,可以验证您对堆栈上内容的假设。我在代码中添加了这个函数,并立即发现在构造函数中的lua_setglobal
之前堆栈为空。从那里开始,一切都开始变得清晰明了。 - cdhowie