3

Programming in Lua の 13.2 章では、次のように述べられています。

算術メタメソッドとは異なり、リレーショナル メタメソッドは混合型をサポートしません。

同時に

Lua は、比較される 2 つのオブジェクトがこのメタメソッドを共有する場合にのみ、等値メタメソッドを呼び出します。

だから私は自分のライブラリを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
4

2 に答える 2

4

編集: C APIでの実装に関して特定の警告がある人の回答も参照してください。__eq


メタ__eqメソッドは、テーブルではなくメタテーブルに属し__indexます。

ルアで:

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

__eqA のメタテーブルではなく、別の無関係なメタメソッド ( ) で使用しているまったく別のテーブルにあることに注意してください__index。の等価演算子を解決しようとするとき、Lua はそこを調べませんa

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)

ここには、メタメソッドを介して解決される resolvea.__eqになるパスがないことがわかります。myLib_A__index

于 2015-09-17T19:17:26.203 に答える