lua-users home
lua-l archive

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


Hi,

> Regarding Wins reply to my previous query about metatables (not)
> having metatables, that was very informative, thanks. However, it did
> not really help with the problem at hand.

Yes, I admit your problem wasn't entirely clear to me from your last mail,
so I made a (semi-)educated guess...  :-)

[... snip snip ... lots of stuff deleted ...]

> Back to the problem: What I would really like to have is a mechanism
> whereby a metatable can redirect calls to metamethods in a way that
> they actually appear as undefined when they are not defined in
> whatever place they are redirected to. Does anyone have an idea how I
> could achieve this?

All this sounds to me as if you more or less want to mimic the Python class
system.  The code below comes a long way.  Classes are created with the
"class" function.  Classes can be subclassed using the "subclass" class
method.  As in Python I use the function call on a class to create an
instance.  If the class (or a superclass) provides an "__init" method this
will sere as a constructor.

Also take a look at the example using addition.  I followed the Python rule
here to try a __radd ("reverse add") method on the argument if an instance
fails to provide an __add method itself.  This is only an example, you can
implement it differently of course.  (There are some nags with this "double
dispatching" of binary operators, but that is a different story altogether.)
Anyway, here's is some code:

-- a fairly simplistic Pythonesque class system setup...

local class_proto = {}
local class_meta = {}
local instance_meta = {}

function class_proto:subclass(table)
    if getmetatable(self) ~= class_meta then
        print "subclass expects a class."
        return
    end
    table.__super = self
    setmetatable(table, class_meta)
    return table
end

function class_meta:__index(index)
    local super = rawget(self, "__super")
    if super then return super[index] end
    return class_proto[index]
end

function class_meta:__call(...)
    local ins = {__class = self}
    setmetatable(ins, instance_meta)
    local init = self.__init
    if init then init(ins, unpack(arg)) end
    return ins
end

function instance_meta:__index(index)
    local class = rawget(self, "__class")
    return class and class[index]
end

function instance_meta:__add(other)
    local add = self.__add
    if add then return add(self, other) end
    add = other.__radd
    if add then return add(other, self) end
    print "__add not supported"
end

-- note:  other arithmetic &c. is similar...

function class(table)
    setmetatable(table, class_meta)
    return table
end

-- let's give it a spin!

-- create a class hierarchy...
ca = class {
    __init = function(self, x) self.x = x end,
}

cb = ca:subclass {
    __add = function(self, other) return self.x + other.x end,
    __radd = function(self, other) return self.x + other.x end,
}

-- create instances...
a = ca(3)
a2 = ca(7)
b = cb(5)

-- perform some arithmetic...
print(a + b)
print(b + a)

-- but this will fail...
print(a + a2)


Maybe this helps a bit more!

Bye,
Wim