eliw00d
eliw00d

Reputation: 331

Metamethods and Classes

I created a similar function to what is shown here, and am having trouble with the __add metamethod. I want to be able to use the __add metamethod on two instances of a Class, but the only way it seems to work is if I add the metamethods to the metatable of the instances. Is there a way I could set this up to work so that my class or its metatable can have the __add metamethod and work when adding instances together?

function Class(superClass, constructor)
    local class = {};
    local metatable = {};

    if superClass then
        metatable.__index = superClass;
    end

    setmetatable(class, metatable);

    function metatable:__call(...)
        local instance = {};
        setmetatable(instance, { __index = class });

        if class.constructor then
            class.constructor(instance, ...);
        end

        return instance;
    end

    class.constructor = constructor;

    return class;
end

Here is an example of what I would like to do:

A = Class(nil, function(self, num)
    self.num = num;
end);

function A:__add(rhs)
    return A(self.num + rhs.num);
end

a = A(1);
b = A(2);

c = a + b;

Upvotes: 1

Views: 135

Answers (2)

user3314993
user3314993

Reputation: 287

Just in case it helps, this is how I usually create my classes (supports inheritance easily, too):

function Vector(x, y)
  local self = {}
  self.x = x
  self.y = y
  self.magnitude = math.sqrt(x*x + y*y)

  function self.unit()
    return Vector(x/self.magnitude, y/self.magnitude)
  end

  function self.printComponents()
    print("[" .. self.x .. ", " .. self.y .. "]")
  end

  --if you want to implement metamethods...
  setmetatable(self, {
    __add = function(_, other)
      return Vector(self.x + other.x, self.y + other.y)
    end
  })

  return self
end

-- now, let's make a ComplexNumber class that inherits from Vector

function ComplexNumber(a, b)
  -- define self as an instance of Vector
  local self = Vector(a, b)

  -- override Vector.printComponents()
  function self.printComponents()
    print(self.x .. " + " .. self.y .. "i")
  end

  return self
end

--examples
local v = Vector(10, 20)
v.printComponents() --> [10, 20]

local c = ComplexNumber(10, 5)
c.printComponents() --> 10 + 5i

Upvotes: 0

eliw00d
eliw00d

Reputation: 331

Thanks to the help of Colonel Thirty Two and Etan Reisner, I came up with the following solution:

function metatable.__call(self, ...)
    local instance = {};
    setmetatable(instance, self);

    if class.constructor then
        class.constructor(instance, ...);
    end

    return instance;
end

I changed the function so that it no longer hides the self variable, and use it as the metatable for the instance. Adding now works as intended. However, it caused issues elsewhere.

I tried a few things I found elsewhere and came up with this working solution:

function Class(superClass, constructor)
    local class = {};

    if superClass then
        for k,v in pairs(superClass) 
        do
            class[k] = v;
        end

        class._superClass = superClass;
    end

    class.__index = class;

    local metatable = {};

    function metatable:__call(...)
        local instance = {};
        setmetatable(instance, class);

        if class.constructor then
            class.constructor(instance, ...);
        end

        return instance;
    end

    class.constructor = constructor;

    setmetatable(class, metatable);

    return class;
end

Upvotes: 1

Related Questions