Вызов функций lua из.lua использует дескрипторы?

Я работаю над небольшим проектом, пытающимся интегрировать lua с C++. Моя проблема, однако, заключается в следующем:

У меня есть несколько сценариев lua, давайте назовем их s1.lua s2.lua и s3.lua. Каждый из них имеет следующие функции: setVars() и executeResults().

Теперь я могу вызывать файл lua через LuaL_dofile и сразу после использования setVars () и / или executeResults(). Однако проблема в том, что после загрузки s2.lua я больше не могу вызывать функции s1.lua. Это означало бы, что я должен повторить LuaL_dofile на s1.lua, чтобы восстановить доступ к функции, и в результате я потеряю доступ к функциям в s2.lua.

Есть ли способ просто загрузить все файлы lua подряд и впоследствии начать вызывать их функции по желанию? Что-то вроде s1->executeResults() s5->executeResults() s3->setVars() и т. Д.

В настоящее время у меня уже есть система, использующая boost::filesystem для обнаружения всех файлов lua в папке, затем я сохраняю имена этих файлов в векторе, а затем просто перебираю вектор, чтобы загрузить каждый файл lua в строке.

Прежде чем заполнить вектор именами файлов lua, моя функция загрузки плагина выглядит так:

void Lua_plugin::load_Plugins(){
 std::vector<std::string>::const_iterator it;
 for (it=Lua_PluginList.begin(); it!=Lua_PluginList.end(); it++){
  std::cout<<"File loading: " << *it << std::endl;
  std::string filename =  *it;
  std::string filepath = scriptdir+filename;
  if (luaL_loadfile(L, filepath.c_str()) || lua_pcall(L, 0, 0, 0)) {
   std::cout << "ScriptEngine: error loading script. Error returned was: " << lua_tostring(L, -1) << std::endl;
  }
 }
}

Чтобы было немного яснее, все, что у меня есть в.lua, примерно так:

-- s1.lua

setVars()
--do stuff
end

executeResults()
--dostuff
end

и т.д., но я хотел бы иметь возможность вызывать s1.lua setVars () и s2.lua setVars () после простой загрузки обоих в ряд.

3 ответа

Решение

Это эффективно то, что Gwell предложил с использованием C API:

#include <stdio.h>

#include "lua.h"

static void
executescript(lua_State *L, const char *filename, const char *function)
{
    /* retrieve the environment from the resgistry */
    lua_getfield(L, LUA_REGISTRYINDEX, filename);

    /* get the desired function from the environment */
    lua_getfield(L, -1, function);

    return lua_call(L, 0, 0);
}

static void
loadscript(lua_State *L, const char *filename)
{
    /* load the lua script into memory */
    luaL_loadfile(L, filename);

    /* create a new function environment and store it in the registry */
    lua_createtable(L, 0, 1);
    lua_getglobal(L, "print");
    lua_setfield(L, -2, "print");
    lua_pushvalue(L, -1);
    lua_setfield(L, LUA_REGISTRYINDEX, filename);

    /* set the environment for the loaded script and execute it */
    lua_setfenv(L, -2);
    lua_call(L, 0, 0);

    /* run the script initialization function */
    executescript(L, filename, "init");
}

int
main(int argc, char *argv[])
{
    lua_State *L;
    int env1, env2;

    L = (lua_State *) luaL_newstate();
    luaL_openlibs(L);

    loadscript(L, "test1.lua");
    loadscript(L, "test2.lua");

    executescript(L, "test1.lua", "run");
    executescript(L, "test2.lua", "run");
    executescript(L, "test2.lua", "run");
    executescript(L, "test1.lua", "run");

    return 0;
}

Тестовые сценарии:

-- test1.lua
function init() output = 'test1' end
function run() print(output) end

-- test2.lua
function init() output = 'test2' end
function run() print(output) end

Выход:

test1
test2
test2
test1

Для краткости я опустил всю обработку ошибок, но вы захотите проверить возвращаемое значение luaL_loadfile и использовать lua_pcall вместо lua_call,

Вы могли бы создать новое состояние lua_newstate() для каждого файла. Это было бы проще, чем мой предыдущий ответ. Тем не менее, это может иметь снижение производительности.

setfenv() Функция может быть использована для создания песочницы или среды для каждого загруженного файла.

Этот пример показывает, что все три файла могут быть загружены конфликтующими функциями и что функции могут быть вызваны в любом порядке. Подобный код может быть написан на C++. Этот пример экспортирует только функцию печати в каждую среду, в вашем сценарии может потребоваться больше.

function newEnv()
  -- creates a simple environment
  return {["print"]=print}
end

local e={} -- environment table
local c    -- chunk variable

-- first instance
c = loadstring([[function f() print("1") end]])
e[#e+1] = newEnv()
setfenv(c, e[#e]) -- set the loaded chunk's environment
pcall(c) -- process the chunk (places the function into the enviroment)

-- second instance
c = loadstring([[function f() print("2") end]])
e[#e+1] = newEnv()
setfenv(c, e[#e])
pcall(c)

-- third instance
c = loadstring([[function f() print("3") end]])
e[#e+1] = newEnv()
setfenv(c, e[#e])
pcall(c)

pcall(e[3].f) --> 3
pcall(e[2].f) --> 2
pcall(e[1].f) --> 1
pcall(e[1].f) --> 1
pcall(e[2].f) --> 2
pcall(e[3].f) --> 3

Другие вопросы по тегам