Thread Tools Display Modes
08-05-14, 09:17 AM   #1
zork
A Pyroguard Emberseer
 
zork's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2008
Posts: 1,740
local function or object:Function, what to pick?

Is there any major benefit of using one over the other?

I asked this in another thread lately and Phanx posted the prototype trick which is kind of neat.

local function variant

Lua Code:
  1. local function ResetAllModels()
  2.     for i, model in pairs(self.models) do
  3.       ModelReset(model)
  4.     end
  5.   end    
  6.    
  7.   local function ModelReset(...)
  8.     print("ModelReset",...)
  9.   end
  10.  
  11.   local function ModelOnMouseDown(...)
  12.     print("ModelOnMouseDown",...)
  13.   end
  14.  
  15.   local function ModelOnMouseUp(...)
  16.     print("ModelOnMouseUp",...)
  17.   end
  18.  
  19.   local function CreateModel(parent,id)
  20.     local m = CreateFrame("PlayerModel", nil, parent)
  21.     m.id = id
  22.     m.name = "model"..id
  23.     m:SetScript("OnMouseDown", ModelOnMouseDown)
  24.     m:SetScript("OnMouseUp", ModelOnMouseUp)
  25.   end
  26.  
  27.  
  28.   local frame = CreateFrame("Frame")
  29.  
  30.   frame.models = {}
  31.  
  32.   for i=1, 50 do
  33.     frame.models[i] = CreateModel(frame,i)
  34.   end

object:Function variant

Lua Code:
  1. local frame = CreateFrame("Frame")
  2.    
  3.   function frame:ResetAllModels
  4.     for i, model in pairs(self.models) do
  5.       model:Reset()
  6.     end
  7.   end    
  8.    
  9.   frame.models = {}
  10.  
  11.   --prototype
  12.   frame.modelPrototype = {}
  13.  
  14.   function modelPrototype:Reset(...)
  15.     print("Reset",...)
  16.   end
  17.  
  18.   function modelPrototype:OnMouseDown(...)
  19.     print("OnMouseDown",...)
  20.   end
  21.  
  22.   function modelPrototype:OnMouseUp(...)
  23.     print("OnMouseUp",...)
  24.   end
  25.  
  26.   function frame:CreateModel(id)
  27.     local m = CreateFrame("PlayerModel", nil, self)
  28.     m.id = id
  29.     m.name = "model"..id
  30.     for k, v in pairs(self.modelPrototype) do
  31.       m[k] = v
  32.     end
  33.     m:SetScript("OnMouseDown", m.OnMouseDown)
  34.     m:SetScript("OnMouseUp", m.OnMouseUp)
  35.   end  
  36.  
  37.   for i=1, 50 do
  38.     frame.models[i] = frame:CreateModel(i)
  39.   end

What I'm asking for is the following. If I add a function to an object...does it produce multiple copies of that function or is it by reference?

Using a local function set has the benefit that the function is only defined once but most of the time you need to add self as an argument which you do not need on object functions.

Well...event handler functions will still use self as an argument.

What do you pick and why?
__________________
| Simple is beautiful.
| WoWI AddOns | GitHub | Zork (WoW)

"I wonder what the non-pathetic people are doing tonight?" - Rajesh Koothrappali (The Big Bang Theory)
  Reply With Quote
08-05-14, 09:36 AM   #2
p3lim
A Pyroguard Emberseer
 
p3lim's Avatar
AddOn Author - Click to view addons
Join Date: Feb 2007
Posts: 1,710
Code:
frame.models[i] = frame:CreateModel(i)
That right there is a reference.

I pick whatever suits the project, sometimes I use a frame with methods, mostly I do local functions.
It really depends if I want it for the sake of wanting it, or when I want them accessible outside of the local scope (as by reference to the global name of the frame(s))

Last edited by p3lim : 08-05-14 at 09:41 AM.
  Reply With Quote
08-05-14, 10:35 AM   #3
Vlad
A Molten Giant
 
Vlad's Avatar
AddOn Author - Click to view addons
Join Date: Dec 2005
Posts: 793
local function requires that you have it defined before you use it - and it might be a bit difficult if you call functions regardless of order of definition. The bonus when using an object is the order doesn't mater, hurray! Though there are other down sides, I find this upside good enough for most addons.
__________________
Profile: Curse | Wowhead
  Reply With Quote
08-05-14, 10:47 AM   #4
Sharparam
A Flamescale Wyrmkin
 
Sharparam's Avatar
AddOn Author - Click to view addons
Join Date: Oct 2011
Posts: 102
You can get around the "not defined" issue by forward-declaring functions:

lua Code:
  1. local b
  2.  
  3. local function a() b() end
  4.  
  5. function b() print 'foo' end

Last edited by Sharparam : 08-05-14 at 10:49 AM. Reason: Declaring `a` not really needed
  Reply With Quote
08-05-14, 12:17 PM   #5
SDPhantom
A Pyroguard Emberseer
 
SDPhantom's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2006
Posts: 2,323
When you get right down to it, performance-wise, defining a function as a local will reference it immediately upon calling. Calling a function as a method (table:func()) causes an indexing operation on table to find func() in order to call it. As such, declaring a function as a method takes slightly longer to call than a function declared as a local.
__________________
WoWInterface AddOns
"All I want is a pretty girl, a decent meal, and the right to shoot lightning at fools."
-Anders (Dragon Age: Origins - Awakening)
  Reply With Quote
08-05-14, 12:35 PM   #6
zork
A Pyroguard Emberseer
 
zork's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2008
Posts: 1,740
Will the following return the same result/performance?
How does it differ?

Prototype
Lua Code:
  1. --prototype
  2.   local modelPrototype = {}
  3.  
  4.   function modelPrototype:Reset(...)
  5.     print("Reset",...)
  6.   end
  7.  
  8.   local function CreateModel(id)
  9.     local m = CreateFrame("PlayerModel")
  10.     for k, v in pairs(modelPrototype) do
  11.       m[k] = v
  12.     end
  13.   end  
  14.  
  15.   for i=1, 50 do
  16.     CreateModel(i)
  17.   end

Adding the :Reset() function onto each object.

Lua Code:
  1. local function CreateModel(id)
  2.     local m = CreateFrame("PlayerModel")
  3.     function m:Reset(...)
  4.       print("Reset",...)
  5.     end
  6.   end  
  7.  
  8.   for i=1, 50 do
  9.     CreateModel(i)
  10.   end

I assume that the later adds individual functions on each object while the prototype example reuses the function. Is that correct? That would make the latter bad if you need to generate alot of objects with alot of functions. Right?
__________________
| Simple is beautiful.
| WoWI AddOns | GitHub | Zork (WoW)

"I wonder what the non-pathetic people are doing tonight?" - Rajesh Koothrappali (The Big Bang Theory)

Last edited by zork : 08-05-14 at 12:37 PM.
  Reply With Quote
08-05-14, 12:55 PM   #7
SDPhantom
A Pyroguard Emberseer
 
SDPhantom's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2006
Posts: 2,323
In order to do that efficiently and if you need the function as a method, I would suggest the following trick I usually do when I need an upvalue to exist only to a select group of functions. Technicly, this is the prototype method using scope as an isolator.

Lua Code:
  1. local CreateModel;--    Function Prototype
  2. do--    New scope for function-specific upvalues
  3.     local function Reset(self,...)
  4.         print("Reset",...);
  5.     end
  6.  
  7.     function CreateModel(id)--  This writes to the local defined outside our do scope
  8.         local m=CreateFrame("PlayerModel");
  9.         m.Reset=Reset;
  10.     end
  11. end
  12.  
  13. for i=1, 50 do
  14.     CreateModel(i);
  15. end

This is tricky, but works. Reset() is defined so that it's only visible to CreateModel() because both are defined in the do...end scope. Because CreateModel() is an upvalue from the do...end scope, that function remains visible to the rest of the code. Since Reset() is only referenced and no longer dynamically created, all models created by CreateModel() will reference the same singular function.



To answer your question directly, with the way both code is generated, they're both assigning the function as methods and the only real difference this time is how you're assigning them to the model's table. The first one is iterating through a table and creating references to your functions, the second one is wasting memory by dynamically creating functions for each model created. This is all about the performance of the model factory and not really the performance of calling a function or calling a method.
__________________
WoWInterface AddOns
"All I want is a pretty girl, a decent meal, and the right to shoot lightning at fools."
-Anders (Dragon Age: Origins - Awakening)

Last edited by SDPhantom : 08-05-14 at 01:15 PM.
  Reply With Quote
08-05-14, 08:49 PM   #8
Phanx
Cat.
 
Phanx's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2006
Posts: 5,617
The reason I suggested using a "prototype" (which is just a convention, Lua doesn't actually care what you call the table, and doesn't treat it specially at all) instead of a bunch of local functions was because it's easier to do this:

Code:
for k, v in pairs(prototype) do
    obj[k] = v
end
than this:

Code:
obj.DoSomething = DoSomething
obj.DoSomethingElse = DoSomethingElse
obj.Cats = Cats
obj.CatsEverywhere = CatsEverywhere
obj.UpdateAllTheThings = UpdateAllTheThings
obj.Over9000Cats = Over9000Cats
obj.Yawn = Yawn
obj.OMGWhereDoesItEnd = OMGWhereDoesItEnd
obj.SoMuchTyping = SoMuchTyping
obj.DoNotWant = DoNotWant
It's easier to write, easier to read, easier to understand at a glance, and easier to maintain -- if you want to add, remove, or rename methods in the future, you just change the methods; you don't have to also change the list of function assignments.

But you seem to actually be asking why I suggested this:

Code:
local function DoSomething(self)
    print("I'm doing something!")
end

local function MakeAThing()
    local thing = CreateFrame("Frame")
    thing.DoSomething = DoSomething
    return thing
over this:

Code:
local function MakeAThing()
    local thing = CreateFrame("Frame")
    function thing:DoSomething()
        print("I'm doing something"!)
    end
    return thing
end
... and the reason is that the second way is a total waste of memory. There's absolutely no reason to create more than one copy of the same function. You can attach it to as many objects as you want, call it from as many different places as you want, as many times as you want, etc. but only one copy of it needs to exist.
__________________
Retired author of too many addons.
Message me if you're interested in taking over one of my addons.
Don’t message me about addon bugs or programming questions.

Last edited by Phanx : 08-05-14 at 08:52 PM.
  Reply With Quote
08-06-14, 02:45 AM   #9
zork
A Pyroguard Emberseer
 
zork's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2008
Posts: 1,740
Thanks you two. That was exactly what I wanted to know.
__________________
| Simple is beautiful.
| WoWI AddOns | GitHub | Zork (WoW)

"I wonder what the non-pathetic people are doing tonight?" - Rajesh Koothrappali (The Big Bang Theory)

Last edited by zork : 08-06-14 at 04:21 AM.
  Reply With Quote

WoWInterface » Developer Discussions » Lua/XML Help » local function or object:Function, what to pick?

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off