Skip to content

Instantly share code, notes, and snippets.

@aurora
Created November 11, 2010 10:51
Show Gist options
  • Save aurora/672338 to your computer and use it in GitHub Desktop.
Save aurora/672338 to your computer and use it in GitHub Desktop.
class implementation for lua
function clone(object)
local dict = {}
local function clone(object)
if (type(object) ~= "table") then
return object
elseif (dict[object]) then
return dict[object]
end
dict[object] = {}
for k, v in pairs(object) do
dict[object][k] = clone(v)
end
return setmetatable(dict[object], clone(getmetatable(object)))
end
return clone(object)
end
local meta_methods = {
__add = true, __sub = true, __mul = true, __div = true, __mod = true, __pow = true, __unm = true,
__concat = true, __len = true,
__eq = true, __lt = true, __le = true,
__index = false, __newindex = false, __call = false, __gc = false
}
function class(parent)
local class = {__meta__ = {}}
if (type(parent) == 'table') then
-- clone parent class
class = clone(parent)
else
parent = nil
-- initialize base class
function class:__construct(...)
end
function class:__destruct(...)
end
function class:__clone(...)
end
end
class.__meta__.__index = class
class.__meta__.__gc = function(...)
self:__destruct(...)
end
setmetatable(class, {
__call = function(self, ...)
local instance = {}
setmetatable(instance, class.__meta__)
instance:__construct(...)
return instance
end,
__newindex = function(t, k, v)
if (meta_methods[k] == nil or type(v) ~= 'function') then
rawset(class, k, v)
elseif (meta_methods[k]) then
class.__meta__[k] = v
else
error("unable to set property")
end
end
})
-- add additional methods, that should be available for each class
-- clone instance
function class:clone(...)
local class = clone(self)
class:__clone(...)
return class
end
-- check instance of some other class
function class:instanceof(class2)
if (class == class2) then
return true
elseif (type(parent) == 'table') then
return parent:instanceof(class2)
else
return false
end
end
-- layer for accessing parent methods in local scope
function class:parent()
return setmetatable({}, {
__newindex = function(...)
error('unable to modify parent')
end,
__index = function(_, k)
local v = rawget(parent, k)
if (type(v) == 'function') then
-- make sure, that parent class is called in child's scope
return function(_, ...)
v(self, ...)
end
elseif (v) then
-- return parent property value
return parent[k]
else
-- unable to resolve parent
return nil
end
end
});
end
return class
end
-- a
a = class()
a.value = 'a'
function a:print()
print('class a : ', self.value)
end
function a:setWorld()
self.value = self.value .. " world!"
end
-- b
b = class(a)
b.value = 'b'
function b:print()
print('class b : ', self.value)
end
function b:setHallo()
self.value = 'hallo!'
self:setWorld()
end
-- c
c = class(b)
c.value = 'c'
function c:print()
print('class c : ', self.value)
self:parent():print()
self:setHallo()
print('class c : ', self.value)
print(self:parent().value)
end
-- test
test_a = a()
test_a:print()
test_b = b()
test_b:print()
test_c = c()
test_c:print()
-- instanceof
print(a:instanceof(a), a:instanceof(b), a:instanceof(c))
print(b:instanceof(a), b:instanceof(b), b:instanceof(c))
print(c:instanceof(a), c:instanceof(b), c:instanceof(c))
-- number
number = class()
number.value = 0
function number:__construct(value)
self.value = value
end
function number:__clone(value)
self.value = value
end
-- cast object of instance 'number' to a lua type number
function number:cast(v)
if (type(v) == 'table' and v:instanceof(number)) then
v = v.value
elseif (type(op1) ~= 'number') then
v = 0
end
return v
end
function number:__add(op)
op = self:cast(op)
return self:clone(self.value + op)
end
----
n1 = number(10)
n2 = number(20)
print(n1.value, ' + ', n2.value, ' = ', (n1 + n2).value)
-- money
money = class(number)
money.currency = 'EUR'
function money:__construct(value, currency)
self:parent():__construct(value)
self.currency = currency or self.currency
end
function money:cast(v)
if (type(v) == 'table' and v:instanceof(money)) then
if (self.currency ~= v.currency) then
error('unable to calculate with operands of different currency!\n')
end
v = v.value
else
v = self:parent():cast(v)
end
return v
end
----
m1 = money(10)
m2 = money(20)
m3 = money(30, 'USD')
sum = m1 + m2
print(m1.value, ' + ', m2.value, ' = ', sum.value)
print(sum.value, ' + ', m3.value, ' = ', (sum + m3).value)
@lucassardois
Copy link

The parent function fail on multiple inheritence:

a = class()
function a:__construct()
  print("a")
end

b = class(a)
function b:__construct()
  -- Doesn't call a:__construct, not ok
  -- result in a stackoverflow
  self:parent():__construct()
  print("b")
end

c = class(b)
function c:__construct()
  -- Call b:__construct, it's ok
  self:parent():__construct()
  print("c")
end

c()

Any ideas on how to fix this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment