【问题标题】:Equality operator on mixed types in LuaLua中混合类型的等式运算符
【发布时间】:2015-09-17 18:36:40
【问题描述】:

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 类型的用户数据

更新: 元表注册:

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

【问题讨论】:

  • 你用的是什么版本的lua?
  • 我正在尝试同时支持 5.1 和 5.2
  • 您是在对象的元表中还是在对象的方法/等中设置__eq?表本身?
  • @whoever:据我所知,您发布的代码应该可以工作,也许您可​​以发布 MWE?
  • @EtanReisner 我正在使用旧式 luaL_register(L, NULL, mylib_A) 来注册元表、luaL_getmetatable 和 lua_setmetatble 以创建用户数据对象,如果这有助于使其更清晰

标签: lua lua-api


【解决方案1】:

编辑:另请参阅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

请注意,__eq 不是在 A 的元表中,它位于一个完全独立的表上,您只是碰巧在一个不同的、不相关的元方法中使用 (__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 的路径,它将通过您的__index 元方法解析为myLib_A

【讨论】:

  • 是的,感谢您的澄清,我真的希望它检查 __index'ed 表。但现在似乎还有更多内容:我重组了代码以将 A 的 __index 自我分配给 A 本身并在 A 中注册所有函数(所以它就像 luaL_newmetatable, luaL_register, lua_setfield(L, -1, "__index" ) 现在),这在 instance_of_A_1 == instance_of_A_2 之间有效,但仍然没有在 instance_of_A == instance_of_B 上被调用,有什么想法吗?
  • new_A 和 new_B 都创建新的用户数据并通过 luaL_getmetatable、lua_setmetatable 为它们分配适当的元表
  • 另外,getmetatable(a)["__eq"](a, b) 调用适当的方法,getmetatable(b)["__eq"](a, b) 调用适当的方法,但 getmetatable (a)["__eq"] == getmetatable(window)["__eq"] 计算结果为假! (我想这是它没有被调用的一个原因)我在这里有点不知所措,有什么想法吗?
  • 不知道你为什么仍然提到__index。它与__eq 问题无关,因此只会分散注意力。确保您没有尝试通过元方法解析元方法。 __eq 应该是直接在您的元表上的字段。 getmetatable(window) 对我来说毫无意义。这是你第一次提到window
  • window 声明 b 那里,只有在评论编辑的 5 分钟截止日期后才注意到它。由于您对getcomphandler 实现的解释,我实际上设法找到了原始问题。我什至不确定是否需要切换已接受的答案,您能否在一开始就链接到我的答案,以便其他人找到它?
【解决方案2】:

对于所有面临同样问题的其他人:
这是我让 Lua 意识到 my_equal 在这两种情况下与 Lua 点完全相同的函数并因此从 getcomphandler 返回正确运算符的唯一方法。以任何其他方式注册它(包括单独的luaL_Reg)都不起作用,因为my_equalluaL_register 上被保存在不同的闭包下,我在这里通过只创建一次闭包来避免这种情况。

// 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

【讨论】:

    猜你喜欢
    • 2017-08-26
    • 1970-01-01
    • 2015-05-24
    • 1970-01-01
    • 2017-06-13
    • 1970-01-01
    • 2012-10-28
    • 1970-01-01
    • 2019-05-14
    相关资源
    最近更新 更多