4

独自のクラスシステムを実装しましたが、問題が発生してい__tostringます。他のメタメソッドでも同様の問題が発生する可能性があると思いますが、試していません。

(簡単な迂回:各クラスには、__classDictすべてのメソッドを保持する属性があります。これは、クラスインスタンスとして使用されます__index。同時に、__ classDict__indexはスーパークラス__classDictであるため、スーパークラスのメソッドは自動的に検索されます。)

すべてのインスタンスで「デフォルトのtostring」動作が必要でした。しかし、それは機能しませんでした。「tostring」動作は、サブクラスを正しく「伝播」しません。

私は自分の問題を例示するこのテストを行いました:

mt1 = {__tostring=function(x) return x.name or "no name" end }
mt2 = {}
setmetatable(mt2, {__index=mt1})
x = {name='x'}
y = {name='y'}
setmetatable(x, mt1)
setmetatable(y, mt2)
print(x) -- prints "x"
print(mt2.__tostring(y)) -- prints "y"
print(y) -- prints "table: 0x9e84c18" !!

その最後の行に「y」を印刷してもらいたいです。

Luaの「to_String」の動作は、同等のものを使用している必要があります

rawget(instance.class.__classDict, '__tostring')

と同等のことをする代わりに

instance.class.__classDict.__tostring

すべてのメタメソッドで同じことが起こると思います。rawget-同等の操作が使用されます。

私ができることの1つは、サブクラス化を行うときにすべてのメタメソッドをコピーすることだと思います(上記の例と同等のものが実行されますmt2.__tostring = mt1.__tostring)が、それは一種のエレガントではありません。

誰かがこの種の問題と戦ったことがありますか?あなたのソリューションはどこにありますか?

4

4 に答える 4

4

すべてのメタメソッドで同じことが起こると思います。rawgetと同等の操作が使用されます。

それは正しいです。luaマニュアルから:

...と読む必要がありますrawget(getmetatable(obj) or {}, event)。つまり、メタメソッドへのアクセスは他のメタメソッドを呼び出さず、メタテーブルのないオブジェクトへのアクセスは失敗しません(単にnilになります)。

通常、各クラスには独自のメタテーブルがあり、関数へのすべての参照をそのクラスにコピーします。つまり、mt2.__tostring = mt1.__tosting

于 2010-07-21T14:16:32.207 に答える
1

daurnimatorのコメントのおかげで、メタメソッドを「フォロー」する方法を見つけたと思い__indexます。それはこの関数に凝縮されています:

local metamethods = {
  '__add', '__sub', '__mul', '__div', '__mod', '__pow', '__unm', '__concat', 
  '__len', '__eq', '__lt', '__le', '__call', '__gc', '__tostring', '__newindex'
}

function setindirectmetatable(t, mt) 
  for _,m in ipairs(metamethods) do
    rawset(mt, m, rawget(mt,m) or function(...)
      local supermt = getmetatable(mt) or {}
      local index = supermt.__index
      if(type(index)=='function') then return index(t,m)(...) end
      if(type(index)=='table') then return index[m](...) end
      return nil
    end)
  end

  return setmetatable(t, mt)
end

私はそれが十分に簡単であることを望みます。新しいメタテーブルが設定されると、すべてのメタメソッドで初期化されます(既存のメタメソッドを置き換えることはありません)。これらのメタメソッドは、リクエストを「親メタテーブル」に「渡す」ために用意されています。

これは私が見つけることができる最も簡単な解決策です。さて、私は実際に使用する文字が少なく、少し速い解決策を見つけましたが、それは黒魔術(自分の体の中で自分自身を参照解除するメタテーブル関数を含む)を含み、これよりもはるかに読みにくくなりました。

誰かが同じことをするより短くてより単純な関数を見つけたら、私は喜んで彼に答えを与えます。

使い方は簡単です。 「上げたい」ときにsetmetatable置き換えます。setindirectmetatable

mt1 = {__tostring=function(x) return x.name or "no name" end }
mt2 = {}
setmetatable(mt2, {__index=mt1})
x = {name='x'}
y = {name='y'}
setmetatable(x, mt1)
setindirectmetatable(y, mt2) -- only change in code
print(x) -- prints "x"
print(mt2.__tostring(y)) -- prints "y"
print(y) -- prints "y"

警告の小さな言葉:setindirectmetatablemt2にメタメソッドを作成します。コピーが作成され、mt2が変更されないままになるようにその動作を変更することは、簡単なことです。しかし、実際には、デフォルトで設定する方が私の目的には適しています。

于 2010-07-26T17:38:59.533 に答える
0

Lua 5.1での私の経験から、メタメソッドはrawget()を使用してメタテーブルで検索されるため、作成するすべてのクラステーブルに関数への参照をコピーする必要があります。

于 2013-02-14T11:15:39.180 に答える
-1

LuaユーザーWikiの継承チュートリアルを参照してください。

于 2010-07-19T16:35:47.050 に答える