Lua 中混合类型的相等运算符
Equality operator on mixed types in Lua
In chapter 13.2 of Programming in Lua据说
Unlike arithmetic metamethods, relational metamethods do not support mixed types.
同时
Lua calls the equality metamethod only when the two objects being compared share this metamethod
所以我正在用 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
编辑:另请参阅 ,其中有关于在 C API.
中实现 __eq
的特别警告
__eq
元方法属于您的元table,不属于 __index
table.
在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 的 metatable 中 不是 ,它在一个完全独立的 table 上,你恰好是使用不同的、不相关的元方法 (__index
)。 Lua 在尝试解析 a
.
的相等运算符时不会查看那里
Lua 手册对此有详细解释:
"eq": the == operation. The function getcomphandler defines how Lua chooses a metamethod for comparison operators. A metamethod only is selected when both objects being compared have the same type and the same metamethod for the selected operation.
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
The "eq" event is defined as follows:
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
。
对于将面临同样问题的所有其他人:
这是我让 Lua 意识到 my_equal
在这两种情况下从 Lua 的角度来看是完全相同的函数并因此从 getcomphandler
返回正确的运算符的唯一方法。以任何其他方式(包括单独的 luaL_Reg
)注册它都不起作用,因为 my_equal
在 luaL_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
In chapter 13.2 of Programming in Lua据说
Unlike arithmetic metamethods, relational metamethods do not support mixed types.
同时
Lua calls the equality metamethod only when the two objects being compared share this metamethod
所以我正在用 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
编辑:另请参阅
__eq
的特别警告
__eq
元方法属于您的元table,不属于 __index
table.
在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 的 metatable 中 不是 ,它在一个完全独立的 table 上,你恰好是使用不同的、不相关的元方法 (__index
)。 Lua 在尝试解析 a
.
Lua 手册对此有详细解释:
"eq": the == operation. The function getcomphandler defines how Lua chooses a metamethod for comparison operators. A metamethod only is selected when both objects being compared have the same type and the same metamethod for the selected operation.
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
The "eq" event is defined as follows:
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
。
对于将面临同样问题的所有其他人:
这是我让 Lua 意识到 my_equal
在这两种情况下从 Lua 的角度来看是完全相同的函数并因此从 getcomphandler
返回正确的运算符的唯一方法。以任何其他方式(包括单独的 luaL_Reg
)注册它都不起作用,因为 my_equal
在 luaL_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