In chapter 13.2 of Programming in Lua它声明
与算术元方法不同,关系元方法不支持混合类型。
并且同时
只有当被比较的两个对象共享这个元方法时,Lua才会调用相等元方法
所以我在C中实现我的库并希望能够支持像
这样的行为a = A()
b = B()
a == b
提供
static const struct luaL_Reg mylib_A[] =
{
{ "__eq", my_equal }
, <more stuff>
, { NULL, NULL }
};
和
static const struct luaL_Reg mylib_B[] =
{
{ "__eq", my_equal }
, <more stuff>
, { NULL, NULL }
};
这似乎不起作用,是否有解决方法?
注意:my_equal能够处理A类型的用户数据和任何一个参数中的B类
更新: Metatables注册:
luaL_newmetatable(lua, "B");
lua_pushvalue(lua, -1);
lua_setfield(lua, -2, "__index");
luaL_register(lua, NULL, mylib_B);
luaL_newmetatable(lua, "A");
lua_pushvalue(lua, -1);
lua_setfield(lua, -2, "__index");
luaL_register(lua, NULL, mylib_A);
luaL_register(lua, "mylib", mylib); -- where mylib is a bunch of static functions
申请代码:
require 'mylib'
a = mylib.new_A()
b = mylib.new_B()
a == b -- __eq is not called
答案 0 :(得分:4)
编辑:另请参阅whoever's answer,其中有关于在C API中实施__eq
的特别警告。
__eq
元方法属于您的元表,而不属于__index
表。
在lua:
function my_equal(x,y)
return x.value == y.value
end
A = {} -- luaL_newmetatable(lua, "A");
A.__eq = my_equal
function new_A(value)
local a = { value = value }
return setmetatable(a, A)
end
B = {} -- luaL_newmetatable(lua, "B");
B.__eq = my_equal
function new_B(value)
local b = { value = value }
return setmetatable(b, B)
end
a = new_A()
b = new_B()
print(a == b) -- __eq is called, result is true
a.value = 5
print(a == b) -- __eq is called, result is false
你所做的是:
myLib_A = {}
myLib_A.__eq = my_equal
A = {} -- luaL_newmetatable(lua, "A");
A.__index = myLib_A
请注意,在{A>的元表中,__eq
不是,它位于一个完全独立的表中,您恰好正在使用另一个不相关的元方法(__index
) 。在尝试解析a
的等式运算符时,Lua不会去那里看。
Lua手册详细解释了这一点:
“eq”:==操作。函数getcomphandler定义了Lua如何为比较运算符选择元方法。当被比较的两个对象具有相同的类型和相同的元方法时,选择元方法。
function getcomphandler (op1, op2, event)
if type(op1) ~= type(op2) then return nil end
local mm1 = metatable(op1)[event]
local mm2 = metatable(op2)[event]
if mm1 == mm2 then return mm1 else return nil end
end
“eq”事件的定义如下:
function eq_event (op1, op2)
if type(op1) ~= type(op2) then -- different types?
return false -- different objects
end
if op1 == op2 then -- primitive equal?
return true -- objects are equal
end
-- try metamethod
local h = getcomphandler(op1, op2, "__eq")
if h then
return (h(op1, op2))
else
return false
end
end
所以当Lua遇到result = a == b
时,它将执行以下操作(这是在C中完成的,Lua在此处用作伪代码):
-- Are the operands are the same type? In our case they are both tables:
if type(a) ~= type(b) then
return false
end
-- Are the operands the same object? This comparison is done in C code, so
-- it's not going to reinvoke the equality operator.
if a ~= b then
return false
end
-- Do the operands have the same `__eq` metamethod?
local mm1 = getmetatable(a).__eq
local mm2 = getmetatable(b).__eq
if mm1 ~= mm2 then
return false
end
-- Call the `__eq` metamethod for the left operand (same as the right, doesn't really matter)
return mm1(a,b)
您可以看到此处没有导致解决a.__eq
的路径,该路径将通过您的myLib_A
元方法解析为__index
。
答案 1 :(得分:2)
对于所有其他将面临同样问题的人:
这是我在这两种情况下让Lua意识到my_equal
与Lua完全相同的函数,从而从getcomphandler
返回正确的运算符的唯一方法。由于luaL_Reg
在my_equal
下的不同闭包下保存,因此以任何其他方式注册它(包括单独的luaL_register
)都不起作用,我在此避免仅创建一次闭包。 / p>
// we'll copy it further to ensure lua knows that it's the same function
lua_pushcfunction(lua, my_equal);
luaL_newmetatable(lua, "B");
// removed __index for clarity
luaL_register(lua, NULL, mylib_B);
// Now we register __eq separately
lua_pushstring(lua, "__eq");
lua_pushvalue(lua, -3); // Copy my_equal on top
lua_settable(lua, -3); // Register it under B metatable
lua_pop(lua, 1);
luaL_newmetatable(lua, "A");
// removed __index for clarity
luaL_register(lua, NULL, mylib_A);
lua_pushstring(lua, "__eq");
lua_pushvalue(lua, -3); // Copy my_equal on top
lua_settable(lua, -3); // Register it under A metatable
luaL_register(lua, "mylib", mylib); // where mylib is a bunch of static functions