Lua C API - загрузка нескольких файлов с переменными и функциями с одинаковыми именами

Скажем, у меня есть два файла Lua, которые я буду использовать из стандартного Lua C API, которые имеют общую библиотеку:

common.lua

function printHello(name)
    print("Hello from " .. name)
end

file1.lua

require "common"
local scriptName = "file1"

function doSomething()
    printHello(scriptName)
end

file2.lua

require "common"
local scriptName = "file2"

function doSomething()
    printHello(scriptName)
end

Теперь предположим, что я хочу, чтобы оба файла * .lua использовали один и тот же lua_State. Не меняя кода Lua, как я могу загрузить файлы таким образом, чтобы я мог вызывать конкретный doSomething()?

Есть ли способ переместить «все» из загруженных файлов (функций, переменных, таблиц) в глобальную таблицу в lua_State, используя имя сценария (или что-то еще) в качестве ключа? Кроме того, есть ли способ сделать это так, чтобы file1.lua и file2.lua могли совместно использовать версию common.lua "в памяти"?

Я использую Lua 5.1.

Спасибо!


person Addy    schedule 08.05.2020    source источник
comment
То, как вы это делаете, сильно изменилось между Lua 5.1 и 5.2. Какую версию Lua вы используете?   -  person Joseph Sible-Reinstate Monica    schedule 08.05.2020
comment
@ JosephSible-ReinstateMonica 5.1. Я тоже обновил вопрос.   -  person Addy    schedule 08.05.2020
comment
Ваша жизнь будет намного проще, если вы определите локальные функции и вернете их вместо того, чтобы определять глобальные переменные и полагаться на игры с глобальной средой.   -  person lhf    schedule 08.05.2020
comment
Вы можете отдать _G каждому file*.lua, реализовав свой собственный require. И _G1, и _G2 унаследуют стандартные глобальные объекты Lua и глобальные объекты, созданные common.lua   -  person Egor Skriptunoff    schedule 08.05.2020
comment
@ JosephSible-ReinstateMonica Если вернуться к этому пару месяцев спустя, как это будет сделано в Lua 5.2? Если хотите, я могу создать новый вопрос.   -  person Addy    schedule 29.08.2020
comment
@Addy Это, наверное, новый вопрос.   -  person Joseph Sible-Reinstate Monica    schedule 29.08.2020


Ответы (1)


Вот как вы это делаете в чистом Lua 5.1:

file1_env = setmetatable({}, {__index = _G})
local file1_chunk = loadfile('file1.lua')
setfenv(file1_chunk, file1_env)
file1_chunk()

file2_env = setmetatable({}, {__index = _G})
local file2_chunk = loadfile('file2.lua')
setfenv(file2_chunk, file2_env)
file2_chunk()

file1_env.doSomething() -- prints "Hello from file1"
file2_env.doSomething() -- prints "Hello from file2"

Что происходит, так это то, что вы меняете среду, в которой работает каждый фрагмент файла, поэтому вместо того, чтобы помещать свои doSomething функции в глобальную среду и, таким образом, подавлять друг друга, они переходят в свою собственную локальную среду (которая использует метатаблицу, чтобы они могли использовать вещи, которые находятся в глобальной среде, например require и print). И, как и было запрошено, common.lua нужно запускать только один раз, что вы увидите, если поставите что-то вроде printHello('common') в конце.

Если вы хотите сделать это из C, все функции, которые я использовал, можно напрямую преобразовать в C API, например:

#include <lua5.1/lua.h>
#include <lua5.1/lualib.h>
#include <lua5.1/lauxlib.h>

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

    /* stack is empty */
    lua_createtable(L, 0, 1);
    /* -1: file1_env */
    lua_createtable(L, 0, 1);
    /* -2: file1_env, -1: file1_env_mt */
    lua_pushvalue(L, LUA_GLOBALSINDEX);
    /* -3: file1_env, -2: file1_env_mt, -1: _G */
    lua_setfield(L, -2, "__index");
    /* -2: file1_env, -1: file1_env_mt */
    lua_setmetatable(L, -2);
    /* -1: file1_env */
    luaL_loadfile(L, "file1.lua");
    /* -2: file1_env, -1: file1_chunk */
    lua_pushvalue(L, -2);
    /* -3: file1_env, -2: file1_chunk, -1: file1_env */
    lua_setfenv(L, -2);
    /* -2: file1_env, -1: file1_chunk */
    lua_call(L, 0, 0);
    /* -1: file1_env */
    lua_setglobal(L, "file1_env");
    /* stack is empty */
    lua_createtable(L, 0, 1);
    /* -1: file2_env */
    lua_createtable(L, 0, 1);
    /* -2: file2_env, -1: file2_env_mt */
    lua_pushvalue(L, LUA_GLOBALSINDEX);
    /* -3: file2_env, -2: file2_env_mt, -1: _G */
    lua_setfield(L, -2, "__index");
    /* -2: file2_env, -1: file2_env_mt */
    lua_setmetatable(L, -2);
    /* -1: file2_env */
    luaL_loadfile(L, "file2.lua");
    /* -2: file2_env, -1: file2_chunk */
    lua_pushvalue(L, -2);
    /* -3: file2_env, -2: file2_chunk, -1: file2_env */
    lua_setfenv(L, -2);
    /* -2: file2_env, -1: file2_chunk */
    lua_call(L, 0, 0);
    /* -1: file2_env */
    lua_setglobal(L, "file2_env");
    /* stack is empty */
    lua_getglobal(L, "file1_env");
    /* -1: file1_env */
    lua_getfield(L, -1, "doSomething");
    /* -2: file1_env, -1: file1_env.doSomething */
    lua_call(L, 0, 0);
    /* -1: file1_env */
    lua_pop(L, 1);
    /* stack is empty */
    lua_getglobal(L, "file2_env");
    /* -1: file2_env */
    lua_getfield(L, -1, "doSomething");
    /* -2: file2_env, -1: file2_env.doSomething */
    lua_call(L, 0, 0);
    /* -1: file2_env */
    lua_pop(L, 1);
    /* stack is empty */

    lua_close(L);
    return 0;
}
person Joseph Sible-Reinstate Monica    schedule 08.05.2020
comment
Должен ли я в C API сохранять соответствующие file1_env и file2_env1 в глобальную таблицу, чтобы я мог получать их по мере необходимости? - person Addy; 08.05.2020
comment
Я не понимаю, как local file1_env = setmetatable({}, {__index = _G}) и local file1_env = setmetatable({}, {__index = _G}) переводятся в C API? Как мне загрузить файл в переменную через C API? - person Addy; 09.05.2020
comment
@Addy Я только что отредактировал, как вы бы сделали то же самое в C. - person Joseph Sible-Reinstate Monica; 09.05.2020
comment
Это будет очень полезно. Большое спасибо. - person Addy; 09.05.2020