-
Notifications
You must be signed in to change notification settings - Fork 243
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
stack overflow due to recursion in instance:base() #79
Comments
here's a minimal example showing it: -- animal.lua
class = require 'pl.class'
--------Animal----------
local Animal = class()
function Animal:_init(name)
self:reset()
self.name = name
end
function Animal:reset()
self.legs = nil
end
---------Cat------------
local Cat = class(Animal)
function Cat:_init(name,breed)
self:super(name) -- must init base!
self.breed = breed
end
function Cat:reset()
self:base("reset")
self.legs = 4
end
function Cat:speak(text)
return 'meow' .. tostring(text)
end
---------Lion------------
local Lion = class(Cat)
function Lion:speak(text)
return 'Roar...' .. tostring(text)
end
----- Try it -------
local pussycat = Lion("pussycat", "mix-match") it gives me;
|
It seems to happen with an instance of the 3rd class in the chain; In my own usecase I have; |
Yes, it's a bug in |
provide the ancestor as an upvalue? something like this: change signature of local function base_method(self,method,...) into local function base_method(self, base, method,...) And then change the implementation to use Then when constructing replace c.base = base_method with c.base = function(self, ...) return base_method(self, base, ...) end I find the code very terse, hence I didn't provide a PR becasue I might do more harm than good. |
That's a good solution - I didn't think of using an upvalue! Was about to suggest we trash this cute method, but your scheme will work. |
Alas, doesn't fly. The only solution I can think of is expensive, and needs the debug library to find what method we're calling from, and what the class of that method is. |
We're doing this all to avoid the straightforward (and fast) |
If that is the solution then it now extends to 3 methods of calling a base class (this method, using |
I don't get the code. Try this code: -- animal.lua
class = require 'pl.class'
--------Animal----------
local Animal = class()
function Animal:_init(name)
self:reset()
self.name = name
end
function Animal:reset()
self.legs = nil
end
---------Cat------------
local Cat = class(Animal)
function Cat:_init(name,breed)
self:super(name) -- must init base!
self.breed = breed
end
function Cat:reset()
--self:base("reset")
self.legs = 4
end
function Cat:speak(text)
return 'meow' .. tostring(text)
end
---------Lion------------
local Lion = class(Cat)
function Lion:speak(text)
return 'Roar...' .. tostring(text)
end
----- Try it -------
---[[
local register = {}
local function reg(name, value)
register[value] = register[value] or name
if type(value) == "table" and getmetatable(value) then
reg(name.."_MT", getmetatable(value))
end
end
local function req(value)
if register[value] then
return "---> "..register[value]
else
return value
end
end
local function printit(t, name)
print(name)
for k,v in pairs(t) do print("",k,req(v)) end
if getmetatable(t) then
print(name.."_MT;", req(getmetatable(t)))
for k,v in pairs(getmetatable(t)) do print("",k,req(v)) end
end
end
reg("Animal", Animal)
reg("Cat", Cat)
reg("Lion", Lion)
local pussycat = Lion("pussycat", "mix-match")
reg("pussycat", pussycat)
printit(Animal, "Animal")
printit(Cat, "Cat")
printit(Lion, "Lion")
printit(pussycat, "pussycat") Then you get the following:
It seems very complex to me. There are lots of Can't it be done by simply making the metatable Or am I missing something? (probably, because it seems to simple 😄) |
These things all start out simple ;) In essence, not complicated:
But then we wanted convenience ( So, even if we used 'inheritance through chaining' I bet you the result would not look that much simpler...that was a deliberate trade-off, so that long inheritance chains would not be penalized. A mess? Yes, I'm afraid that the |
I tried several things, but only to conclude that |
While testing I also ran into a bug with local A = class()
function A:_init()
self.init_chain = "A"
end
local B = class(A)
local C = class(B)
function C:_init()
self:super()
self.init_chain = self.init_chain.."C"
end
local D = class(C)
local E = class(D)
function E:_init()
self:super()
self.init_chain = self.init_chain.."E"
end
local F = class(E)
local G = class(F)
function G:_init()
self:super()
self.init_chain = self.init_chain.."G"
end
local i = G()
assert(i.init_chain == "ACEG") I get this when running;
This is because |
so if the convenience function |
I think I've rescued super: -- this trickery is necessary to prevent the inheritance of 'super' and
-- the resulting recursive call problems.
local function call_ctor (c,obj,...)
-- nice alias for the base class ctor
local base = rawget(c,'_base')
if base then
local parent_ctor = rawget(base,'_init')
while base and not parent_ctor do
base = rawget(base,'_base')
parent_ctor = rawget(base,'_init')
end
if parent_ctor then
rawset(obj,'super',function(obj,...)
call_ctor(base,obj,...)
end)
end
end
local res = c._init(obj,...)
rawset(obj,'super',nil)
return res
end That is, go for a hunt for the first base class with I am trying to save |
gave it some thought today. An alternative would be to make sure that every class, upon creation, gets an |
That was my first try. It did work, and was straightforward, But your By the way, I can no longer remember the purpose of No longer convinced that the new |
Yes it seems so, but how often is this used? my guess is very little (the properties class is probably the only implementation of it). So maybe reduce on the overhead and have the For me just a class with |
I am liking the idea of nice and simple at the moment ;) I've just patched Let me look at |
This seems to work as a workaround;
it seems the call
self:base("reset")
is the culprit causing the recursion.Any ideas what might be the problem?
The text was updated successfully, but these errors were encountered: