#c #lua #embedded-language
#c #lua #встроенный язык
Вопрос:
Рассмотрим следующий код C с использованием Lua C API:
#include <string>
#include <cassert>
#include <lua/lua.hpp>
class AwesomeThing
{
lua_State* _lua;
std::string _name;
public:
AwesomeThing(lua_State* L, const std::stringamp; name, const std::stringamp; 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()
at lua_getfield(_lua, 1, "onInit")
: PANIC: незащищенная ошибка при вызове Lua API (попытка индексировать нулевое значение)
Я чувствую, что есть что-то фундаментальное, чего мне не хватает в том, как я справляюсь с песочницей. Я изо всех сил старался следовать нескольким другим примерам изолированной обработки Lua 5.2, которые я нашел в Интернете, но пока ничего не помогло.
Ответ №1:
После того, как я сам поработал с кодом, я смог это исправить, и ошибки, похоже, возникают всего из-за нескольких ошибок.
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
, просто очистите стек, заменив оба всплывающих вызова на lua_settop(_lua, 0)
.
Комментарии:
1. Я только что понял, как это сделать, используя некоторые Lua с
luaL_dostring
, и все еще не был уверен, что я делаю не так с моим кодом C, но это объясняет это. Большое спасибо!2. @Addy Рад помочь!
lua_type()
в частности, это очень полезно для проверки ваших предположений о том, что находится в стеке. Я разбросал это повсюду и сразу обнаружил, что раньше стек был пустlua_setglobal
в конструкторе. С этого момента все начало собираться воедино.