Sharing global variables between different Lua states through require

前端 未结 3 1803
[愿得一人]
[愿得一人] 2021-01-23 04:30

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 exampl

3条回答
  •  夕颜
    夕颜 (楼主)
    2021-01-23 05:00

    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 
    
    #include 
    
    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(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(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 
    #include 
    #include 
    
    #include 
    
    #include 
    
    enum class nil {};
    using Variant = boost::variant;
    
    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 *>(
            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(lua_toboolean(L, 3));
            lua_pop(L, 1);
            break;
        case LUA_TNUMBER:
            if (lua_isinteger(L, 3)) {
                v = static_cast(luaL_checkinteger(L, 3));
            } else {
                v = static_cast(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 *>(
            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(v));
            break;
        case 2:
            lua_pushinteger(L, boost::get(v));
            break;
        case 3:
            lua_pushnumber(L, boost::get(v));
            break;
        case 4:
            lua_pushstring(L, boost::get(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 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);
    }
    

提交回复
热议问题