lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


Your SetInheritance example function is flawed if you are only trying to
modify the one object.
It sounds like this may not be your actual code, but just a distilled
example, however...

nmt is meaningless.

  obj.omt = getmetatable(sf)
  nmt = obj.omt
  nmt.__index = -- A Function

Note that "nmt.__index = " is equivalent to "obj.omt.__index = " since in
fact nmt is a reference to the same table as obj.omt.


I think what you wanted was more along the lines of...

SetInheritance = function(obj, parent)
  local nmt = {}
  local omt = getmetatable(obj)

  nmt.__index = function(object, key)
    local omt = omt       -- Correct use of upvalues
    local parent = parent -- to store the original
                          -- metatable and parent?
    local result

    if (omt) then
      -- There should be a simpler way to
      -- express this lookup.
      if (istable(omt.__index) then
        result = omt.__index[key]
      else
        result = omt.__index(object, key)
      end
    end

    if result then return result end

    return parent[key]
  end

  -- Presumably we need a C function to call
  -- setmetatable for us because Lua code
  -- isn't allowed to set metatables for
  -- a userdata
  application:SetObjectMetatable(obj, nmt)
end

--
Of course I'm confused by what sf and parent were supposed to be in your
original function.
Also, this is further out into metatables and closures than I've really
ventured before so take this with a large grain of salt.
Thanks for the interesting thought excersize though!
Maybe my suggestion will even be useful.   :-)

Note, however, that I've made no attempt here to handle members of the
metatable OTHER than __index.  A really "simple" way of handling this would
be to set a metatable for nmt that contains omt as its __index member.
However, this will only work if the implementation of retrieving a
metamethod from a metatable is done with the equivalent of a normal table
lookup rather than the equivalent of a rawget (and that is something that I
do NOT know off hand).

If metamethod lookups are done via a rawget equivalent then a full table
copy of the original metatable will be required to achieve your goal (and my
accesses to omt.__index should be rewritten for consistency with "normal"
behavior when accessing a metatable).


----- Relevant snippets of original message -----

SetInheritance = function(obj, parent)
  local nmt

  obj.omt = getmetatable(sf)
  nmt = obj.omt
  nmt.__index = function(object, key)
    local result = rawget(object, "omt")[key]
    if result then return result end
    return parent[key]
  end
  application:SetObjectMetatable(sf, nmt)
end


But I was intentionally trying just to modify obj's inheritance and not
the behavior of all objects that use the same metatable that obj
originally uses.

Conceptually I have a userdata object whose behavior is
implemented/defined by its metatable:

obj
    omt

I'm basically looking to tack on another one:

obj
    omt  (if obj usage is handled here, do so and be done)
       nmt   (else, use this metatable...)



-----Original Message-----
From: lua-bounces@bazar2.conectiva.com.br
[mailto:lua-bounces@bazar2.conectiva.com.br]On Behalf Of Ando Sonenblick
Sent: Tuesday, December 16, 2003 8:14 PM
To: Lua list
Subject: patching userdata metatable.... ouch!


Gang,

I'm continually impressed with lua and its flexibility. So I intuitively
know what I want to do is possible, but I keep asking more and more tweaky
things of lua and get more and more confused the more abstract of a thing I
try. And again, I need help getting my head around this one.

OK, I have a userdata "object": obj I have a metatable associate with it so
it can have methods and data (obj:Foo(), obj.bar = 4)

Goal: I now want to have it inherent from another object: x, where is is a
table or a userdata

x = { Bar = function() print("bar") end }

So that after calling SetInhereitance(obj, x) I can then calll: obj:Bar()...

So I want to patch in a new metatable that will, for any __index call (to
start with) it will first call the original metatable and if something is
found, return that. Otherwise it'll index x to see if it has anything,
returning the result.

But now matter what I try I cant get this to work. I think I have a solution
were obj a table but not for user data. For illustration purposes, this is
the direction I've been going:

SetInheritance = function(obj, parent)
local nmt

obj.omt = getmetatable(sf)
nmt = obj.omt
nmt.__index = function(object, key)
local result = rawget(object, "omt")[key]
if result then return result end
return parent[key]
end
application:SetObjectMetatable(sf, nmt)
end

I *think* this conceptually encapsulates (heh, no pun intended) my approach.
But obviously the rawget doesnt work on a userdata type. And If I try
object.omt[key] I of course get a recursive stack overflow...

I'm pretty sure there is a way to accomplish this with storing an associate
between obj and its original metatable (omt) in some other table, but I'd
really like to store the original within the userdata itself. But I think I
may be looking down the throat of disappointment on this one. Am I wrong?

Anyone know how to accomplish what I'm looking to do?

thx,
ando