Lua从C中注册metatable的问题

你好,我有下面的代码,它似乎能工作,但我不确定为什么 - 我构建了一个 testclass 如下

class testclass {
    int ivalue;
public:
    int getivalue();
    void setivalue(int &v);
};

然后注册 testclass(实际函数的一些位被省略了,但它们相当基本)。我不理解的是注册 metatable 的部分。(setivalue 和 getivalue 是调用同名类函数的 c 函数)

static const struct luaL_Reg arraylib_f [] = {
    {"new", new_testclass},
    {NULL, NULL}
};

static const struct luaL_Reg arraylib_m [] = {
    {"set", setivalue},
    {"get", getivalue},
    {NULL, NULL}
};

int luaopen_testclass (lua_State *L) {
    luaL_newmetatable(L, "LuaBook.testclass");
    lua_pushvalue(L, -1); /* duplicates the metatable */
    lua_setfield(L, -2, "__index");
    luaL_register(L, NULL, arraylib_m);
    luaL_register(L, "testclass", arraylib_f);
    return 1;
}

我不理解的是,我将函数添加到 metatable 的 __index 中,但是当我运行

a = testclass.new()
a:set(10)
print(a:get())

它就像预期的那样工作。我不明白的是,为什么 set 被调用,我认为我已将其加载到 __index metatable 中了?这是我做的还是其他什么东西?

谢谢回答。

点赞
用户459706
用户459706
int luaopen_testclass (lua_State *L) {
    luaL_newmetatable(L, "LuaBook.testclass"); // 将新的元表留在栈中
    lua_pushvalue(L, -1); // 在栈中有两个元表的“副本”
    lua_setfield(L, -2, "__index"); // 弹出其中的一个并将其赋值给第一个元表的 __index 字段
    luaL_register(L, NULL, arraylib_m); // 在元表中注册函数
    luaL_register(L, "testclass", arraylib_f); // 注册 testclass 类型的函数
    return 1;
}

这段代码相当于下面的 Lua 代码:

metatable = {}
metatable.__index = metatable
metatable.set = function() --[[stuff--]] end
metatable.get = function() --[[stuff--]] end

我认为 new_testclass C 函数会为返回的表格设置元表 "LuaBook.testclass"。

在你的代码中,你没有将函数添加到元表的 __index 字段中。相反,你将指向元表的指针分配给了那个元表的名为 __index 的字段,并将 setget 函数注册到其中。

现在,如果你将该元表设置为从 'new_testclass' 函数返回的值(我认为你这样做了)——我们将其称为 'foo',然后调用 foo:set(10),那么 Lua 将会:

  1. 检查 'foo' 中是否有 'set' 字段
  2. 查看该表是否有一个元表
  3. 查看该元表的 __index 字段——发现它是一个表
  4. 检查分配给 __index 字段的表中是否有一个名为 'set' 且值为函数的字段
  5. 调用 'set' 方法,并将 'foo' 作为 self 参数传递

我希望这会帮助你理解这里发生了什么。

2011-02-23 15:15:29
用户614701
用户614701

如果我理解你的问题正确,你正在问set() get() 是如何通过__index元方法调用的。

该代码可以用纯lua表达:

local o = {}

function o.get(self)
    return self.ivalue
end

function o.set(self, val)
    self.ivalue = val
end

a = {}
mt = {
    __index = function(t, n)
        return o[n]
    end
}

setmetatable(a, mt)

print(a:get())
a:set(10)
print(a:get())

结果:

nil
10

在这个例子中,mt表被设置为a表的元表。由于a表中既没有get表项也没有set表项,因此__index元方法被用于getset

如果将这个例子改成这样:

local o = {}

function o.get(self)
    return self.ivalue
end

function o.set(self, val)
    self.ivalue = val
end

a = {}

function a.get(self)
    print('here')
    return self.ivalue
end

mt = {
    __index = function(t, n)
        return o[n]
    end
}

setmetatable(a, mt)

print(a:get())
a:set(10)
print(a:get())

结果:

here
nil
here
10

在这种情况下,get()不会调用__index元方法,因为a表已经存在get索引。

使用元方法可以创建许多有趣的结构,一旦你理解了它们的工作原理。我建议阅读13.4.1-在PiL中使用__index元方法 并且进行一些其他例子的实践。所有上述操作也可以通过_c api_完成。

2011-02-23 15:20:41