Bonsai Posted June 5, 2016 Share Posted June 5, 2016 Hey Peeps, can I somehow use a method as the function argument for event handlers? I tried something like this but it doesn't work: This is an OOP example from Google: local MyClass = {} MyClass.__index = MyClass setmetatable(MyClass, { __call = function (cls, ...) return cls.new(...) end, }) function MyClass.new(init) local self = setmetatable({}, MyClass) self.value = init return self end -- the : syntax here causes a "self" arg to be implicitly added before any other args function MyClass:set_value(newval) self.value = newval end function MyClass:get_value() return self.value end local instance = MyClass(5) -- do stuff with instance... So imagine one of these methods would handle drawing stuff on the screen. Then I would need to add an event handler that calls it. function MyClass.new(init) local self = setmetatable({}, MyClass) self.value = init addEventHandler("onClientRender", root, self:draw, true, "low-99") return self end At least thats what I thought. But it outputs this error in log: "..function arguments expected near.." Does anyone know how I can make it work? I need each object to have its own handler. Bonsai Link to comment
Discord Moderators AlexTMjugador Posted June 5, 2016 Discord Moderators Share Posted June 5, 2016 As you may already know, self:draw(...) is syntactic sugar for self.draw(self, ...). But self:draw is a expression to call a function, not to refer to a function itself. So self:draw(...) is, in simple terms, like func(...), but self:draw alone does not refer to func because the function arguments are part of its syntax. Therefore, to refer to a function in a method, you have to use self.draw. Link to comment
Bonsai Posted June 5, 2016 Author Share Posted June 5, 2016 Thank you, now it doesn't show that error for "addEventHandler" anymore. But I can't use "self" in the render function like this. Every object has different things to draw, and I need to access them using self. Do you know how to solve that? Bonsai Link to comment
Discord Moderators AlexTMjugador Posted June 5, 2016 Discord Moderators Share Posted June 5, 2016 It is necessary to rethink your code then, as event handlers do not allow to pass some variable to the handler function and onClientRender can't trigger for a specific element. This is the code I came across with. It uses a table to save all the instances of the class, so then the onClientRender event handler can iterate over it and do things. Plus it should be more CPU efficient than adding a handler for every instance. Comments in the code explain what the code does and where. -- Class MT local MyClass = {} MyClass.__index = MyClass MyClass.instances = {} -- Call the constructor when calling the MyClass table itself setmetatable(MyClass, { __call = function (cls, ...) return cls.new(...) end, }) function MyClass.new(init) -- Create new table and let it access our class functions and vars local self = setmetatable({}, MyClass) -- Let the object have a value (it is not saved in the class itself) self.value = init -- Add it into the instances table MyClass.instances[self] = true return self end function MyClass:set_value(newval) -- Delete old entry MyClass.instances[self] = nil -- Set our new value self.value = newval -- Update value in table -- You should do something like this for every value change, so onClientRender gets updated data -- Alternatively, I think that you can use metatables and let them handle this for you, but I think it is too complicated MyClass.instances[self] = true end function MyClass:get_value() return self.value end function MyClass:delete() -- Delete from instance table MyClass.instances[self] = nil -- Delete ourselves self = nil end function drawMyClassInsts() for inst in pairs(MyClass.instances) do -- Whatever you want to do here. To get value: -- value = inst.value end end addEventHandler("onClientRender", root, drawMyClassInsts, true, "low-99") Link to comment
Bonsai Posted June 5, 2016 Author Share Posted June 5, 2016 Yep, thats what I wanted to avoid But seems there is no other way then. Thanks for your detailed answers. Btw, if I create an instance somewhere like this: local object = MyClass.new(init); And I have a destroy method that does something like self = nil inside the class. Will "object" be nil too or do I have to destroy it in a different way? Bonsai Link to comment
Discord Moderators AlexTMjugador Posted June 5, 2016 Discord Moderators Share Posted June 5, 2016 The object variable will still be a table after that, but an unuseful one in this case (it doesn't get rendered), and you should avoid doing things with it. The optimal thing to do is nil'ing it after calling the destructor. But sadly, it is not possible to delete the object in its own destructor in pure Lua. You can read more about it in this Stack Overflow question. Link to comment
Bonsai Posted June 5, 2016 Author Share Posted June 5, 2016 Having to nil it manually isn't that bad after all. I changed it like that now and it works perfectly so far. Now i will change all my other classes to do it the same way. Having only one render function and a loop inside instead of mulitple handlers seems to be a better solution now indeed. Thanks, Bonsai Link to comment
Discord Moderators AlexTMjugador Posted June 6, 2016 Discord Moderators Share Posted June 6, 2016 No problem You might want to add a MyClass:isInstance method to the class, which you can use like isElement, to check if the table is a valid instance of the class or not, and handle deletion better. Link to comment
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now