package.preload alternative in Lua? - c++

I learned that package.preload can be used to expose a script to other scripts.
Here's my example code.
lua_State *L = luaL_newstate();
luaL_openlibs(L);
lua_settop(L, 0);
//Script A
luaL_dostring(L, "local A = {} package.preload['A'] = function () return A end A.num = 3");
//Script B
luaL_dostring(L, "local A = require 'A' print(A.num)");
lua_close(L);
The result: 3
Although this works fine, I wonder if Script A's code can be more simplified or if there's other alternative solution to expose a script to other scripts.
ADDED: The main reason I'm asking this is because I think package.preload['A'] = function () return A end is quite long and boring to write.

In this case, where you have some set of in-C strings that represent Lua modules, package.preload is exactly the tool to use. Though your specific use of it leaves something to be desired.
Generally speaking, the modules themselves do not define their names. So hard-coding the module's name into the string is not the correct move. Similarly, modules do not register themselves; they should be registered by the environment around the module.
What you really want is to take an array of name+Lua code strings and register them as module preloads in a loop. So you'd have something like this. I'll be using Lua 5.3; you can translate it to older version of Lua pretty easily.
Also, be warned: this code is untested.
const char *lua_preloads[] =
{
"A", "local A = {}\n"
"A.num = 3\n"
"return A)\n", //Modules are usually tables, not functions.
...
NULL //Null-terminated list.
};
//Loader function
int lua_preloader_func(lua_State *L)
{
int nargs = lua_gettop(L);
int lua_func_ix = lua_upvalueindex(1);
lua_pushvalue(L, lua_func_ix);
//Move the function to the bottom of the stack
lua_insert(lua_func_ix, 1);
//Call with all of the given arguments.
lua_call(L, nargs, LUA_MULTRET);
return lua_gettop(L);
}
int top = lua_gettop(L);
//Get the package.preload table.
lua_getglobal(L, "package");
lua_getfield(L, -1, "preload");
int preload_ix = lua_gettop();
for(const char **position = lua_preloads;
*position;
position += 2)
{
const char *module_name = position[0];
const char *module = position[1];
//Compile the preload script into a Lua function.
int err = luaL_loadbufferx(L, module, strlen(module), module_name, "t");
//Check for errors in `err`.
//Create a Lua C-function with the script as an upvalue.
lua_pushcclosure(L, lua_preloader_func, 1);
//Stick that Lua C-function inside of package.preload[preload.first].
lua_setfield(L, preload_ix, module_name);
}
lua_settop(L, top);

It seems as if you want to prefix local A = {} package.preload['A'] = function () return A end to every chunk defining a module (where A is the module name). I think it would be much easier to just use string concatenation for that.
#include <string>
#include <lua.hpp>
int preload(lua_State *L, std::string const &modname,
std::string const &modcode) {
std::string code = "package.preload['" + modname + "'] = function()\n" +
"local " + modname + " = {}\n" + modcode + "\n"
"return " + modname + " end";
return luaL_dostring(L, code.c_str());
}
int main() {
lua_State *L = luaL_newstate();
luaL_openlibs(L);
// Script A
preload(L, "A", "A.num = 3");
// Script B
luaL_dostring(L, "local A = require 'A' print(A.num)");
lua_close(L);
}

Related

Handling binary data between C++ and Lua

i need to make a program that will handle binary data, lots of it.
in short, the C++ program will load a binary file (some of them exceed 20mb) into a buffer.
then it will run a Lua script and pass all this loaded data to the script, which will do some manipulation and return the result to the C++ program.
I need to do this as quickly as possible, perform at the best and get the job done faster.
A while ago I already made this program using the conventional methods of Lua, but it was extremely slow.
So I lost the files, and now I want to redo it in a better, faster way that doesn't compromise performance.
Searching a bit, I found this.
I had to make some small changes to adapt to the new version of Lua, but I can't get it to work.
Can you help me with this?
And if there's a better way to do the job I said, what would it be?
#include "stdafx.h"
// metatable method for handling "array[index]"
static int array_index(lua_State* L) {
int** parray = (int**)luaL_checkudata(L, 1, "array");
int index = luaL_checkinteger(L, 2);
lua_pushnumber(L, (*parray)[index - 1]);
return 1;
}
// metatable method for handle "array[index] = value"
static int array_newindex(lua_State* L) {
int** parray = (int**)luaL_checkudata(L, 1, "array");
int index = luaL_checkinteger(L, 2);
int value = luaL_checkinteger(L, 3);
(*parray)[index - 1] = value;
return 0;
}
// create a metatable for our array type
static void create_array_type(lua_State* L) {
static const struct luaL_Reg array[] = {
{ "__index", array_index },
{ "__newindex", array_newindex },
NULL, NULL
};
luaL_newmetatable(L, "array");
luaL_setfuncs(L, array, 0);
}
// expose an array to lua, by storing it in a userdata with the array metatable
static int expose_array(lua_State* L, int array[]) {
int** parray = (int**)lua_newuserdata(L, sizeof(int**));
*parray = array;
luaL_getmetatable(L, "array");
lua_setmetatable(L, -2);
return 1;
}
// test data
int mydata[] = { 1, 2, 3, 4 };
// test routine which exposes our test array to Lua
static int getarray(lua_State* L) {
return expose_array(L, mydata);
}
int __declspec(dllexport) __cdecl luaopen_array(lua_State* L) {
create_array_type(L);
// make our test routine available to Lua
lua_register(L, "array", getarray);
return 0;
}
int main()
{
lua_State* L = luaL_newstate();
luaL_dostring(L, "require 'array'");
luaL_dostring(L, "foo = array()");
luaL_dostring(L, "x = foo[1]");
lua_getglobal(L, "x");
lua_Number x = lua_tonumber(L, 1);
printf("foo[1] = %d\n", (int)x);
}
Consider using lightuserdata to avoid copying file contents excessively.
lightuserdata is just a pointer, so you need to define some methods to work with it as well.
The idea looks like this:
#include <lauxlib.h>
#include <lualib.h>
#define BIN_DATA_MT_ID "bin data"
int get_byte(lua_State *L) {
const char *file_contents = luaL_checkudata(L, 1, BIN_DATA_MT_ID);
size_t byte_index = luaL_checknumber(L, 2);
// checking OOB is your custody here, omitted for simplicity
lua_pushlstring(L, file_contents + byte_index, 1);
return 1;
}
static const luaL_Reg __index[] = {
{"get_byte", get_byte},
{NULL, NULL}
};
int main() {
const char file_contents[4] = { 0x25, 0xAA, 0xBB, 0xCC };
lua_State *L = luaL_newstate();
luaopen_base(L); // adds "print" function
lua_pushlightuserdata(L, (void *)file_contents);
luaL_newmetatable(L, BIN_DATA_MT_ID);
luaL_newlib(L, __index);
lua_setfield(L, -2, "__index");
lua_setmetatable(L, -2);
lua_setglobal(L, "mybindata");
luaL_dostring(L, "print(mybindata:get_byte(0))");
lua_close(L);
return 0;
}
The fastest way for Lua to access the bytes of a large byte array is to expose that array directly as a string within Lua. Now because Lua does reference counting for strings, this means that Lua will always allocate its own storage for the string. So to efficiently load the string into Lua (ie: avoiding a 20+MiB copy), you need to use the lua_Buffer-based API to load it directly into Lua's storage.
But outside of that quirk, it will certainly be faster inside Lua to use array accesses of a string to access bytes compared to doing a function call for each byte accessed from the buffer.

How can I make an iterate function Lua C? [duplicate]

I'm trying to add the LUA API to my C++ program, and I'm attempting to allow the script to draw to my GUI. So far, I have this for my lambda function:
auto addToDrawList = [](lua_State* L) -> int
{
int DrawType = (int)lua_tonumber(L, -2);
std::string Label = (std::string)lua_tostring(L, -1);
bool found = false;
for (int i = 0; i <= DrawList.size(); i++)
{
if (DrawList[i].Active == false && !found)
{
switch (DrawType)
{
case(0):
break;
case(1):
DrawList[i].Active = true;
DrawList[i].DrawType = Type::TextBox;
DrawList[i].Label = Label;
break;
}
found = true;
}
}
return 0;
};
This as my LUA script being run:
const char* LUA_FILE = R"(
addToDrawList(1, "Test")
)";
This is how I'm pushing my function to the LUA stack:
lua_State* L = luaL_newstate();
lua_newtable(L);
int uiTableInd = lua_gettop(L);
lua_pushvalue(L, uiTableInd);
lua_setglobal(L, "Ui");
lua_pushcfunction(L, addToDrawList);
lua_setfield(L, -2, "addToDrawList");
The problem is within my first script, as it can't get to the 'DrawList' array as its inside of this.
So, to resolve it, I tried to add this to the lambda's capture list by doing this:
auto addToDrawList = [this](lua_State* L) -> int
Which appeared to work and resolve the error, but then I had an issue with the last script:
lua_pushcfunction(L, addToDrawList);
I've been searching the Internet for a fix, but I can't find any.
lua_pushcfunction() takes a C-style function pointer. A capture-less lambda can be converted to such a function pointer, but a capturing lambda cannot.
Use lua_pushcclosure()1 instead. It will allow you to associate user-defined values (known as upvalues) with the C function, such as your this pointer, or just a pointer to DrawList, etc.
When a C function is created, it is possible to associate some values with it, thus creating a C closure (see §3.4); these values are then accessible to the function whenever it is called. To associate values with a C function, first these values should be pushed onto the stack (when there are multiple values, the first value is pushed first). Then lua_pushcclosure is called to create and push the C function onto the stack, with the argument n telling how many values should be associated with the function. lua_pushcclosure also pops these values from the stack.
1: lua_pushcfunction() is just a wrapper for lua_pushcclosure() with 0 upvalues defined.
For example:
auto addToDrawList = [](lua_State* L) -> int
{
const MyClassType *pThis = (const MyClassType*) lua_topointer(L, lua_upvalueindex(1));
// use pThis->DrawList as needed...
return 0;
};
...
lua_State* L = luaL_newstate();
...
//lua_pushcfunction(L, addToDrawList);
lua_pushlightuserdata(L, this);
lua_pushcclosure(L, addToDrawList, 1);
...

How to reference 'this' in a lambda used with a LUA script

I'm trying to add the LUA API to my C++ program, and I'm attempting to allow the script to draw to my GUI. So far, I have this for my lambda function:
auto addToDrawList = [](lua_State* L) -> int
{
int DrawType = (int)lua_tonumber(L, -2);
std::string Label = (std::string)lua_tostring(L, -1);
bool found = false;
for (int i = 0; i <= DrawList.size(); i++)
{
if (DrawList[i].Active == false && !found)
{
switch (DrawType)
{
case(0):
break;
case(1):
DrawList[i].Active = true;
DrawList[i].DrawType = Type::TextBox;
DrawList[i].Label = Label;
break;
}
found = true;
}
}
return 0;
};
This as my LUA script being run:
const char* LUA_FILE = R"(
addToDrawList(1, "Test")
)";
This is how I'm pushing my function to the LUA stack:
lua_State* L = luaL_newstate();
lua_newtable(L);
int uiTableInd = lua_gettop(L);
lua_pushvalue(L, uiTableInd);
lua_setglobal(L, "Ui");
lua_pushcfunction(L, addToDrawList);
lua_setfield(L, -2, "addToDrawList");
The problem is within my first script, as it can't get to the 'DrawList' array as its inside of this.
So, to resolve it, I tried to add this to the lambda's capture list by doing this:
auto addToDrawList = [this](lua_State* L) -> int
Which appeared to work and resolve the error, but then I had an issue with the last script:
lua_pushcfunction(L, addToDrawList);
I've been searching the Internet for a fix, but I can't find any.
lua_pushcfunction() takes a C-style function pointer. A capture-less lambda can be converted to such a function pointer, but a capturing lambda cannot.
Use lua_pushcclosure()1 instead. It will allow you to associate user-defined values (known as upvalues) with the C function, such as your this pointer, or just a pointer to DrawList, etc.
When a C function is created, it is possible to associate some values with it, thus creating a C closure (see §3.4); these values are then accessible to the function whenever it is called. To associate values with a C function, first these values should be pushed onto the stack (when there are multiple values, the first value is pushed first). Then lua_pushcclosure is called to create and push the C function onto the stack, with the argument n telling how many values should be associated with the function. lua_pushcclosure also pops these values from the stack.
1: lua_pushcfunction() is just a wrapper for lua_pushcclosure() with 0 upvalues defined.
For example:
auto addToDrawList = [](lua_State* L) -> int
{
const MyClassType *pThis = (const MyClassType*) lua_topointer(L, lua_upvalueindex(1));
// use pThis->DrawList as needed...
return 0;
};
...
lua_State* L = luaL_newstate();
...
//lua_pushcfunction(L, addToDrawList);
lua_pushlightuserdata(L, this);
lua_pushcclosure(L, addToDrawList, 1);
...

How to get preloaded module name in C++

I wonder if it's possible to create a C++ function that returns(or prints) the preloaded module name of the current script.
For example, I would like to create getModuleName() function in the following code so running the code can print A as a result.
#include "lua.hpp"
void main()
{
lua_State *L = luaL_newstate();
luaL_openlibs(L);
lua_settop(L, 0);
luaL_dostring(L, "package.preload['A'] = function ()\n"
"local a = {}\n"
"a.name = my.getModuleName()\n"
"print(a.name)\n"
"return a end");
luaL_dostring(L, "require 'A'\n");
lua_close(L);
}
How can I create a getModuleName() function in C++?
If it's not possible with C++, I would like to know if it's possible to do it in Lua.
P.S: I'm using SWIG for binding C++ code..
require calls the preload function with the name it was given as the first argument.
#include "lua.hpp"
int main() {
lua_State *L = luaL_newstate();
luaL_openlibs(L);
lua_settop(L, 0);
luaL_dostring(L, "package.preload['A'] = function(this)\n"
"local a = {}\n"
"a.name = this\n"
"print(a.name)\n"
"return a end");
luaL_dostring(L, "require 'A'\n");
lua_close(L);
}
$ clang++ -Wall -Wextra -Wpedantic -I /usr/include/lua5.2 test.cpp -llua5.2
$ ./a.out
A
Avoiding to pass and argument
I don't see why you would want to do this but it is easily doable by overriding the require function with your own version. For simplicity I only show the Lua code:
local require_original = require
function require(name, ...)
current_module = name
local val = table.pack(require_original(name, ...))
current_module = nil
return table.unpack(val,1,val.n)
end
package.preload["test"] = function()
print("While loading:", current_module)
return {}
end
print("Before loading:", current_module)
require("test")
print("After loading:", current_module)
$ lua5.2 test.lua
Before loading: nil
While loading: test
After loading: nil
Answer to the misunderstood question
package.preload is just a regular Lua table which you can traverse just as any other Lua table from the C-API. In this case you will have to traverse it twice, once to determine which preloads are already there before adding a new one, and then again after you added your preload.
#include <iostream>
#include <string>
#include <unordered_set>
#include "lua.hpp"
int main() {
lua_State *L = luaL_newstate();
luaL_openlibs(L);
lua_settop(L, 0);
// Determine all existing preloads
std::unordered_set<std::string> known_preloads;
lua_getglobal(L, "package");
lua_getfield(L, -1, "preload");
lua_pushnil(L);
while (lua_next(L, -2) != 0) {
known_preloads.emplace(lua_tostring(L, -2)); // pops key
lua_pop(L, 1); // pops value
}
lua_pop(L, 2); // pop preload and package
// Add a new preload
luaL_dostring(L, "package.preload['A'] = function ()\n"
"local a = {}\n"
"a.name = my.getModuleName()\n"
"print(a.name)\n"
"return a end");
luaL_dostring(L, "require 'A'\n");
// Determine which preloads are new
std::unordered_set<std::string> new_preloads;
lua_getglobal(L, "package");
lua_getfield(L, -1, "preload");
lua_pushnil(L);
while (lua_next(L, -2) != 0) {
std::string current = lua_tostring(L, -2); // pops key
if (known_preloads.find(current) == known_preloads.end()) {
new_preloads.emplace(current);
}
lua_pop(L, 1); // pops value
}
lua_pop(L, 2); // pop preload and package
// Print the new preloads
for (auto const & preload : new_preloads) {
std::cout << preload << '\n';
}
lua_close(L);
}
You might want to consider using Sol2. It's the fastest wrapper around the Lua C-API for C++ that exists. It requires C++14 and it's totally worth it. See how I didn't worry about the stack a single time!
#include <iostream>
#include <string>
#include <unordered_set>
#define SOL_CHECK_ARGUMENTS 1
#include "sol.hpp"
int main() {
sol::state L;
L.open_libraries();
// Determine all existing preloads
std::unordered_set<std::string> known_preloads;
L.get<sol::table>("package").get<sol::table>("preload").for_each(
[&](sol::object &key, sol::object &) {
known_preloads.emplace(key.as<std::string>());
});
// Add a new preload
L.script("package.preload['A'] = function ()\n"
"local a = {}\n"
"a.name = my.getModuleName()\n"
"print(a.name)\n"
"return a end");
L.script("require 'A'\n");
// Determine which preloads are new
std::unordered_set<std::string> new_preloads;
L.get<sol::table>("package").get<sol::table>("preload").for_each(
[&](sol::object &key_, sol::object &) {
std::string key = key_.as<std::string>();
if (known_preloads.find(key) == known_preloads.end()) {
new_preloads.emplace(key);
}
});
// Print the new preloads
for (auto const & preload : new_preloads) {
std::cout << preload << '\n';
}
}

Sharing global variables between different Lua states through require

I'm trying to find a way to share global variables of a specific Lua script(test.lua in the example) between different Lua states.
Here's my simple example code:
In test.lua
num = 2
In main.cpp
#include <iostream>
#include <lua.hpp>
int main()
{
lua_State *L1 = luaL_newstate(); //script A
luaL_openlibs(L1);
lua_settop(L1, 0);
luaL_dostring(L1, "require('test') num = 5");
lua_State *L2 = luaL_newstate(); //script B
luaL_openlibs(L2);
lua_settop(L2, 0);
luaL_dostring(L2, "require('test') print(num)");
lua_close(L1);
lua_close(L2);
}
I expect to get 5 but I get 2.
Is not possible to share global variables between different lua_State* through require?
ADDED :
If it's not possible, would it be a good idea to open test.lua using luaL_loadfile and then create getter/setter methods in C++ to share variable num between script A and B?
For example like this,
Script A:
script = my.Script("test")
script:setVar("num", 5)
Script B:
script = my.Script("test")
print(script:getVar("num"))
I wonder what you think about this design as an alternative to require.
Two distinct lua_States are completely and totally independent. One cannot directly affect anything that happens in another. You can expose some C code to one that allows it to modify the other, or they could both access some external resource (a file, for example) that allows them to share data.
But outside of things like this, no, they cannot interact.
The preferred method for this is to not make them separate lua_States.
Rather than having the global value in a Lua module, you could push a pointer to a C++ value as an upvalue for a metatable to a table which contains those globals. Then you push the globals table with the same metatable to both VMs. When you now access globals.num the getglobal and setglobal metamethods are triggered (depending on whether you read or write). These will update the value on the C++ side, such that it is shared between the two VMs.
N.B.: As you can judge from the lengthy boilerplate this is not a good solution. You should avoid having multiple VMs at the same time. If you require multiple VMs for concurrency purposes, consider using a mature library like Lua Lanes rather than rolling your own (doing this right requires several thousands of lines of code).
#include <string>
#include <lua.hpp>
int setglobal(lua_State *L) {
void *p = luaL_checkudata(L, 1, "globals_meta");
luaL_argcheck(L, p != nullptr, 1, "invalid userdata");
std::string key = lua_tostring(L, 2);
luaL_argcheck(L, key == "num", 2, "unknown global");
int value = luaL_checkinteger(L, 3);
luaL_argcheck(L, lua_isnumber(L, 3), 3, "not a number");
int *num = static_cast<int *>(lua_touserdata(L, lua_upvalueindex(1)));
*num = value;
lua_pop(L, 1);
return 0;
}
int getglobal(lua_State *L) {
void *p = luaL_checkudata(L, 1, "globals_meta");
luaL_argcheck(L, p != nullptr, 1, "invalid userdata");
std::string key = lua_tostring(L, 2);
luaL_argcheck(L, key == "num", 2, "unknown global");
int num = *static_cast<int *>(lua_touserdata(L, lua_upvalueindex(1)));
lua_pop(L, 1);
lua_pushinteger(L, num);
return 1;
}
static const struct luaL_Reg globals_meta[] = {
{"__newindex", setglobal},
{"__index", getglobal},
{nullptr, nullptr} // sentinel
};
int main() {
int num = 2;
// script A
lua_State *L1 = luaL_newstate();
luaL_openlibs(L1);
luaL_newmetatable(L1, "globals_meta");
lua_pushlightuserdata(L1, &num);
luaL_setfuncs(L1, globals_meta, 1);
lua_newuserdata(L1, 0);
luaL_getmetatable(L1, "globals_meta");
lua_setmetatable(L1, -2);
lua_setglobal(L1, "globals");
luaL_dostring(L1, "print('Script A: ' .. globals.num) globals.num = 5");
// script B
lua_State *L2 = luaL_newstate();
luaL_openlibs(L2);
luaL_newmetatable(L2, "globals_meta");
lua_pushlightuserdata(L2, &num);
luaL_setfuncs(L2, globals_meta, 1);
lua_newuserdata(L2, 0);
luaL_getmetatable(L2, "globals_meta");
lua_setmetatable(L2, -2);
lua_setglobal(L2, "globals");
luaL_dostring(L2, "print('Script B: ' .. globals.num)");
lua_close(L1);
lua_close(L2);
}
As a challange to myself I implemented a complete global table which can communicate values of type nil, bool, int, double, and string between two Lua states. They can be named with everything that has a string representation.
-- To be on the safe side, just use numbers and strings as keys
globals[1] = "x"
globals.num = 5
-- Be careful when using table or function literals as keys
-- Two empty tables don't have the same representation
globals[{}] = 2 -- "table: 0x10d55a0" = 2
globals[{}] = 1 -- "table: 0x10ce2c0" = 1
I haven't checked all sorts of exceptional situations exhaustively, so no refunds!
#include <iostream>
#include <string>
#include <unordered_map>
#include <boost/variant.hpp>
#include <lua.hpp>
enum class nil {};
using Variant = boost::variant<nil, bool, int, double, std::string>;
int setglobal(lua_State *L) {
void *p = luaL_checkudata(L, 1, "globals_meta");
luaL_argcheck(L, p != nullptr, 1, "invalid userdata");
std::string key = luaL_tolstring(L, 2, nullptr);
auto &globals = *static_cast<std::unordered_map<std::string, Variant> *>(
lua_touserdata(L, lua_upvalueindex(1)));
Variant &v = globals[key];
switch (lua_type(L, 3)) {
case LUA_TNIL:
v = nil{};
break;
case LUA_TBOOLEAN:
v = static_cast<bool>(lua_toboolean(L, 3));
lua_pop(L, 1);
break;
case LUA_TNUMBER:
if (lua_isinteger(L, 3)) {
v = static_cast<int>(luaL_checkinteger(L, 3));
} else {
v = static_cast<double>(luaL_checknumber(L, 3));
}
lua_pop(L, 1);
break;
case LUA_TSTRING:
v = std::string(lua_tostring(L, 3));
lua_pop(L, 1);
break;
default:
std::string error = "Unsupported global type: ";
error.append(lua_typename(L, lua_type(L, 3)));
lua_pushstring(L, error.c_str());
lua_error(L);
break;
}
return 0;
}
int getglobal(lua_State *L) {
void *p = luaL_checkudata(L, 1, "globals_meta");
luaL_argcheck(L, p != nullptr, 1, "invalid userdata");
std::string key = luaL_tolstring(L, 2, nullptr);
auto globals = *static_cast<std::unordered_map<std::string, Variant> *>(
lua_touserdata(L, lua_upvalueindex(1)));
lua_pop(L, 1);
auto search = globals.find(key);
if (search == globals.end()) {
lua_pushstring(L, ("unknown global: " + key).c_str());
lua_error(L);
return 0;
}
Variant const &v = search->second;
switch (v.which()) {
case 0:
lua_pushnil(L);
break;
case 1:
lua_pushboolean(L, boost::get<bool>(v));
break;
case 2:
lua_pushinteger(L, boost::get<int>(v));
break;
case 3:
lua_pushnumber(L, boost::get<double>(v));
break;
case 4:
lua_pushstring(L, boost::get<std::string>(v).c_str());
break;
default: // Can't happen
std::abort();
break;
}
return 1;
}
static const struct luaL_Reg globals_meta[] = {
{"__newindex", setglobal},
{"__index", getglobal},
{nullptr, nullptr} // sentinel
};
int main() {
std::unordered_map<std::string, Variant> globals;
globals["num"] = 2;
// script A
lua_State *L1 = luaL_newstate();
luaL_openlibs(L1);
luaL_newmetatable(L1, "globals_meta");
lua_pushlightuserdata(L1, &globals);
luaL_setfuncs(L1, globals_meta, 1);
lua_newuserdata(L1, 0);
luaL_getmetatable(L1, "globals_meta");
lua_setmetatable(L1, -2);
lua_setglobal(L1, "globals");
if (luaL_dostring(L1, "print('Script A: ' .. globals.num)\n"
"globals.num = 5") != 0) {
std::cerr << "L1:" << lua_tostring(L1, -1) << '\n';
lua_pop(L1, 1);
}
// script B
lua_State *L2 = luaL_newstate();
luaL_openlibs(L2);
luaL_newmetatable(L2, "globals_meta");
lua_pushlightuserdata(L2, &globals);
luaL_setfuncs(L2, globals_meta, 1);
lua_newuserdata(L2, 0);
luaL_getmetatable(L2, "globals_meta");
lua_setmetatable(L2, -2);
lua_setglobal(L2, "globals");
if (luaL_dostring(L2, "print('Script B: ' .. globals.num)") != 0) {
std::cerr << "L1:" << lua_tostring(L2, -1) << '\n';
lua_pop(L2, 1);
}
lua_close(L1);
lua_close(L2);
}
While Lua states are separate by default, some binding libraries expose functionality to transfer information from one to the other.
For example, in sol, there are methods to serialize fairly arbitrary Lua data, including functions, to C++ data. You can then de-serialize that data into another Lua state, to effectively copy it (code link).
But you still will have two copies, in the end. You can't modify one Lua state from another directly.
Your last point, about exposing some getter/setter, is valid. You can have some data stored in C/C++ and have two different Lua states able to access it. You still have to bind that data to each VM separately.