3

Say I have two Lua files which I'll be using from the standard Lua C API, that share a common library:

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

Now say I want to have both file*.lua files share the same lua_State. Without changing any of the Lua code, how can I load the files in way such that I can call a specific doSomething()?

Is there a way I can move "everything" from the loaded files (functions, variables, tables) it into a global table within the lua_State using the script-name (or whatever) as the key? Also, is there a way I can do this such that file1.lua and file2.lua can share the "in memory" version of common.lua?

I am using Lua 5.1.

Thanks!

6
  • The way you'd do this changed a lot between Lua 5.1 and 5.2. What version of Lua are you using exactly? Commented May 8, 2020 at 16:53
  • @JosephSible-ReinstateMonica 5.1. I've updated the question as well. Commented May 8, 2020 at 16:54
  • Your life will be much easier if you define local functions and return them instead of defining global variables and relying on games with the global environment. Commented May 8, 2020 at 17:00
  • You can give separate _G to each file*.lua by implementing your own require. Both _G1 and _G2 would inherit standard Lua globals and globals created by common.lua Commented May 8, 2020 at 17:00
  • 1
    @Addy That should probably be a new question. Commented Aug 28, 2020 at 23:37

1 Answer 1

2

Here's how you do it in pure 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"

What's going on is you're changing the environment that each file's chunk runs in, so instead of putting their doSomething functions in the global environment and thus stomping each other, they go in their own local environment (which uses a metatable so they can use things that are in the global environment like require and print). And as requested, common.lua only has to run once, as you'll see if you put something like printHello('common') at the end of it.

If you want to do this from C, all of the functionality I used can be straightforwardly converted to the C API, like this:

#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;
}
Sign up to request clarification or add additional context in comments.

4 Comments

In the C API, should I save the corresponding file1_env and file2_env1 into a global table so I can retrieve them as needed?
I don't understand how local file1_env = setmetatable({}, {__index = _G}) and local file1_env = setmetatable({}, {__index = _G}) translate to the C API? How would I load a file into a variable via the C API?
@Addy I just edited in how you'd do the same thing in C.
This will be massively useful. Thank you so much.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.