4

I've got a scripting system working well using userdata objects. However, I now want to have a property on my userdata that can take a regular table.

I think what I should do is create a normal table and set the metatable to use my current set of metamethods, however I'm struggling to understand how to do this - I'm sure it's a simple tweak, I just can't see it right now.

My existing code looks like:

void
LuaContext::push(lua_State* state, boost::shared_ptr<LuaWrapped> wrapped) {
    static struct luaL_Reg methods[] = {
        { "__index", LuaWrapped::static_get },
        { "__newindex", LuaWrapped::static_set },
        { "__len", LuaWrapped::static_len },
        { "__ipairs", LuaWrapped::static_ipairs },
        { "__pairs", LuaWrapped::static_pairs },
        { "__gc", LuaWrapped::static_gc },
        { "__eq", LuaWrapped::static_eq },
        { NULL, NULL }
    };

    LuaWrapped::Ptr **ptr = (LuaWrapped::Ptr **)lua_newuserdata(state, sizeof(LuaWrapped::Ptr *));
    *ptr = new LuaWrapped::Ptr(wrapped);

    if (luaL_newmetatable(state, "LuaWrapped")) {
        lua_pushstring(state, "__index");
        lua_pushvalue(state, -2);
        lua_settable(state, -3);
        luaL_openlib(state, NULL, methods, 0);
    }
    lua_setmetatable(state, -2);
}

The __gc metamethod is in there to delete the LuaWrapped::Ptr class (which is a wrapper to a boost::shared_ptr). I guess I'll leave that along, and store the pointer in a lightuserdata field on the normal table.


Experimental custom metatable against normal table issue (per discussion in comments):

void
LuaContext::push(lua_State* state, boost::shared_ptr<LuaWrapped> wrapped) {
    static struct luaL_Reg methods[] = {
        { "__index", LuaWrapped::static_get },
        { "__newindex", LuaWrapped::static_set },
        { "__len", LuaWrapped::static_len },
        { "__ipairs", LuaWrapped::static_ipairs },
        { "__pairs", LuaWrapped::static_pairs },
        { "__gc", LuaWrapped::static_gc },
        { "__eq", LuaWrapped::static_eq },
        { NULL, NULL }
    };

    lua_newtable(state);
    LuaContext::push(state, "pointer");
    lua_pushlightuserdata(state, new LuaWrapped::Ptr(wrapped));
    lua_settable(state, -3);

    lua_newtable(state);
    luaL_openlib(state, NULL, methods, 0);
    lua_setmetatable(state, -2);
}

int
LuaWrapped::static_get(lua_State* state) {
    int argc = lua_gettop(state);
    for (int i = 1; i <= argc; i++) {
        const char *type = lua_typename(state, i);
        std::cout << type << std::endl;
    }
    ....

Expected output on a get:

table, string

Actual output on a get (Lua 5.2, Ubuntu 14.04):

boolean, userdata

4

1 回答 1

5

将任意数据与用户数据一起存储是用户数据环境/用户值的用途。

lua 5.2 的方法是使用lua_setuservaluelua_getuservalue函数将表与用户数据相关联。然后可以使用该表来存储和检索与用户数据相关的任意值。

在 lua 5.1 中,更通用的环境概念被用于此目的lua_setfenvlua_getfenv但想法是相同的。

于 2015-01-11T23:50:50.557 回答