如何修改由C API创建的元表?

我想向由C API创建的Lua对象添加一些方法或属性。我不能以正常的方式添加属性,例如:

local foo = libc.new()
foo.bar = "hello"

它会报错:

Failed to run script: attempt to index a libc_meta value (local 'foo')

所以我认为可能需要修改元表,所以我改变了我的代码:

local foo = libc.new()
local mt = getmetatable(foo)
foo[bar] = "hello"
setmetable(foo, mt)

不幸的是,它仍然不起作用。

Failed to run script: bad argument #1 to 'setmetatable' (table expected, got libc_meta)

那么如何向这个'foo'添加方法或属性呢?

顺便说一下,这里是C代码:

static int libc_new(lua_State *L) {
    ...
    lua_lib_space *libc = lua_newuserdata(L, sizeof(*libc));
    libc->L = L;
    libc->cb_enter = cb_enter;
    libc->cb_leave = cb_leave;
    luaL_getmetatable(L, "libc_meta");
    lua_setmetatable(L, -2);
    lib_space *lib = lib_new(enter, leave, libc);
    libc->space = lib;
    return 1;
}
点赞
用户734069
用户734069

Userdata 被设计用于由 C 创建、通过 C 代码操纵,并且仅限于 C 代码的目的。Lua 可以与其交互,但仅限于 C 所允许的方式。因此,虽然 userdata 可以有一个元表(并且那些元方法是 Lua 直接与 userdata 交互的唯一方式),但只有 C 函数(还有 debug 库,但那是作弊)可以直接操纵该元表。Lua 是一门嵌入式语言,C 拥有主导地位;如果某个 C 库想要将你挤出去,那它就可以这样做。

可以做的是将 userdata 放入自己的一个表中,然后给该表添加一个元表。在你的 __index 元方法中,如果你具有一个特定键的重写或新方法,则将访问转发到该方法。否则,它将仅访问已存储的 userdata

但是,如果你从 userdata 中得到的是一个函数,那么你可能会有问题。看到了吗,大多数 userdata 函数将 userdata 作为参数;事实上,大多数函数旨在通过 "ud:func_name(params)"的方式调用。但是,如果ud实际上是表包装器,那么传递给函数的第一个参数将是**包装器**而不是userdata` 本身。

这会带来一个问题。当你从 userdata 中得到的是一个函数时,你需要返回一个函数的包装器,该包装器遍历参数,并将任何对包装表的引用转换为实际的 userdata

2019-03-25 14:03:36