通过require在不同的Lua状态之间共享全局变量
Sharing global variables between different Lua states through require
我正试图找到一种方法,在不同的Lua状态之间共享特定Lua脚本(示例中为test.lua
(的全局变量。
下面是我的简单示例代码:
在test.lua
中
num = 2
在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);
}
我希望得到5
,但我得到了2
。
不可能在不同的lua_State*
到require
之间共享全局变量吗?
添加:
如果不可能,那么使用luaL_loadfile
打开test.lua
,然后在C++中创建getter/setter方法,在脚本A
和B
之间共享变量num
,这是一个好主意吗?
例如,
脚本A:
script = my.Script("test")
script:setVar("num", 5)
脚本B:
script = my.Script("test")
print(script:getVar("num"))
我想知道你对require
的替代方案有什么看法。
两个不同的lua_State
是完全独立的。一个人不能直接影响另一个人身上发生的任何事情。您可以将一些C代码公开给其中一个,允许它修改另一个,或者它们都可以访问一些外部资源(例如文件(,允许它们共享数据。
但除了这样的事情,不,他们不能相互作用。
优选的方法是不使它们分离lua_State
s。
您可以将指向C++值的指针作为元表的upvalue推送到包含这些全局值的表,而不是在Lua模块中具有全局值。然后将具有相同元表的globals
表推送到两个VM。当您现在访问globals.num
时,会触发getglobal
和setglobal
元方法(取决于您是读还是写(。这将更新C++端的值,以便在两个VM之间共享。
N。B.:正如你从冗长的样板中所判断的那样,这不是一个好的解决方案。您应该避免同时拥有多个虚拟机。如果出于并发目的需要多个虚拟机,请考虑使用像Lua-Lanes这样的成熟库,而不是滚动自己的库(要做到这一点需要数千行代码(。
#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);
}
作为我自己的挑战,我实现了一个完整的全局表,它可以在两个Lua状态之间传递类型为nil
、bool
、int
、double
和string
的值。它们可以用所有具有字符串表示形式的东西来命名。
-- 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
我没有彻底检查过各种特殊情况,所以没有退款!
#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);
}
虽然Lua状态在默认情况下是分开的,但一些绑定库公开了将信息从一个状态传输到另一个状态的功能。
例如,在sol中,有一些方法可以将相当任意的Lua数据(包括函数(序列化为C++数据。然后,您可以将该数据反序列化为另一个Lua状态,以有效地复制它(代码链接(。
但你最终还是会有两份。您不能直接从另一个状态修改一个Lua状态。
您的最后一点,关于公开某个getter/setter,是有效的。你可以将一些数据存储在C/C++中,并有两个不同的Lua状态可以访问它。你仍然需要将这些数据分别绑定到每个VM。
- C++中std::resize(n)和std::shrink_to_fit之间的区别
- int(c) 和 c-'0' 之间的区别。C++
- 在cuda线程之间共享大量常量数据
- 在c代码之间共享数据的最佳方式
- Mix_Init和Mix_OpenAudio SDL之间的区别是什么
- C++ 使用 assign 函数的字符串与直接使用 '=' 更改值的字符串之间的区别
- OpenCV 函数 cv::remap() 的执行时间更长,当程序在两者之间进入睡眠状态时
- DirectX 9 在重置调用之间保留状态
- 通过require在不同的Lua状态之间共享全局变量
- 在cython调用之间保持数据帧的状态
- 旧状态和新状态之间的区别
- 如何在本地设置和全局设置之间以C 11 RNG的初始状态进行切换
- 主要功能和状态之间的通信
- 在相同状态类型的各种屏幕之间切换
- OpenCL:指令和地址之间的状态空间不匹配
- 状态机状态之间的通信
- 调用 ~Derived() 和 ~Base() 之间的对象状态
- 如何安全地更改状态对象之间的状态
- 多个应用程序之间的数据库状态
- 在来自C++应用程序的成功调用之间保留dll中的状态