将一个userdata元表添加到Lua表中

4

我已经成功地使用userdata对象编写了一个脚本系统。然而,我现在想在我的userdata上有一个可以接受常规表格的属性。

我认为我应该创建一个普通的表,并设置元表以使用我的当前一组元方法,但我很难理解如何做到这一点 - 我相信这只是一个简单的调整,但我现在看不到它。

我的现有代码如下:

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);
}
__gc元方法用于删除LuaWrapped::Ptr类(它是对boost::shared_ptr的包装器)。我想我会让它保持原样,并将指针存储在普通表的lightuserdata字段中。


自定义实验元表与普通表问题(根据评论中的讨论):

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;
    }
    ....

在获取时预期的输出:

表格,字符串

实际获取时的输出(Lua 5.2,Ubuntu 14.04):

布尔值,用户数据


我正在尝试弄清楚为什么要干扰表格。如果目的是将表格存储在用户数据上,那么这与将任何其他值存储在用户数据上没有区别(您只需要将表格与用户数据关联并将引用存储在其中),不需要干扰元表或任何其他内容。您目前如何在用户数据上存储属性? - Etan Reisner
@EtanReisner 我目前所有的属性都是通过在C++对象上调用__index / __newindex方法来处理的。 - Phil Lello
我似乎无法使用rawset/rawget将属性保存到userdata中;我期望如果我使用具有自定义元方法的真实表格,那么rawset/rawget函数将起作用。不幸的是,我在这个方向上的实验并没有取得太好的效果 - 具体来说,在__index/__newindex回调中,我没有得到表格作为参数1。 - Phil Lello
rawget/rawset完全忽略元方法,不会触发它们。你使用它们来避免这些元方法。你也不能在userdata上使用rawset/rawget,这是真的。我不确定rawset/rawget如何与这个对话相关。也许你应该放上一个或多个测试以及它们为什么不起作用? - Etan Reisner
太棒了!如果你想把它作为答案,我很乐意接受。 - Phil Lello
显示剩余5条评论
1个回答

5

在userdata中存储任意数据,就需要使用userdata环境/用户值。

在lua 5.2中,使用lua_setuservaluelua_getuservalue函数来将一个表格与userdata关联起来。这个表格可以用来存储和检索与userdata相关的任意值。

在lua 5.1中,更通用的环境概念是通过lua_setfenvlua_getfenv来实现的,但其思想是相同的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接