Вызов функций 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

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


person Karrok    schedule 07.08.2010    source источник


Ответы (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.

person Judge Maygarden    schedule 09.08.2010

Функцию 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

person gwell    schedule 08.08.2010
comment
Во-первых, спасибо! Я искал часть C ++ lua, и я не могу найти, какая функция заменила бы setfenv () lua в среде C ++. В основном это потому, что я хотел бы сохранить контроль над надстройками / надстройками в моем коде на C ++ (помимо того, что я немного больше разбираюсь в C ++ по сравнению с lua). И правильно ли я предполагаю, что затем я смогу включить создание среды в свою функцию Load_Plugins ()? Имеете ли список среды, используемый другими частями кода c ++? заранее спасибо - person Karrok; 08.08.2010
comment
Обнаружено исправление pgl.yoyo.org/luai/i/lua_setfenv Бросив через lua- В списках пользователей users.org можно было бы предположить, что эта функция устарела, но я все равно попробую :) - person Karrok; 08.08.2010
comment
Устаревание будет зависеть от версии Lua, которую вы используете. setfenv() будет объявлен устаревшим в версии 5.2. См. corsix.org/content/look-lua-52-work3. - person gwell; 08.08.2010
comment
Некоторые пытаются позже, и я не могу понять, как приведенный выше код lua будет переведен на c ++ :(. Я предполагаю, что я неправильно использую lua_setfenv и lua_getfenv, поскольку приложение просто вылетает. - person Karrok; 08.08.2010

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

person gwell    schedule 09.08.2010
comment
Что ж, если создание нового состояния только один раз для каждого файла lua не слишком сильно снижает производительность, тогда это не должно быть проблемой. Вторичный и последующий доступ к состояниям должен происходить не так часто. Я думаю, что попробую новую штуку, потому что я все еще не могу заставить lua_setfenv работать без сбоя приложения :( Еще раз спасибо :) - person Karrok; 09.08.2010