|
10-08-2010, 07:36 AM
|
The Undefeated
|
|
Join Date: Oct 2010
Posts: 8
|
|
And now I'm lost. Current class implementation doesn't create new or copy functions for each instance, they are stored in table that acts like both prototype and instance factory thanks to metatables. Making new copy of function instead of referencing it doesn't make any sense unless we need to access parent scope.
|
10-08-2010, 08:28 AM
|
|
The Undying
|
|
Join Date: Sep 2010
Posts: 207
|
|
Quote:
Originally Posted by Kryso
And now I'm lost. Current class implementation doesn't create new or copy functions for each instance, they are stored in table that acts like both prototype and instance factory thanks to metatables. Making new copy of function instead of referencing it doesn't make any sense unless we need to access parent scope.
|
So let me get this straight, calling a function with a dot operator, i.e.
foo.bar();
will create a copy of that function each time it's called.
However, setting a variable with a dot operator, i.e.
foo.bar = 1;
won't create a copy of the variable each time it's set/used?
|
10-08-2010, 09:49 AM
|
The Undefeated
|
|
Join Date: Oct 2010
Posts: 8
|
|
What? no no no
calling given function with dot operator won't create anything by itself. But the function that created function so we can call it with dot operator and valid self reference will. Example:
Code:
-- Class
Foo = class();
function Foo:Constructor()
self.num = 0;
self.Bar = function( increment )
-- here we can access "self" because we take it from parent scope
self.num = self.num + increment;
end
end
-- Test init
print = Turbine.Shell.WriteLine;
-- Testing code
local instance1 = Foo();
-- those calls won't create anything and we must call this function with ".", ":" would break it
print(" ");
print("Testing . operator")
instance1.Bar( 1 );
instance1.Bar( 2 );
instance1.Bar( 3 );
print( " - " .. tostring( instance1.num ) );
print(" ");
print("Testing : operator")
success, message = pcall( function() instance1:Bar( 3 ); end );
if ( not success ) then
print( " - Error: " .. message );
end
print(" ");
print("Testing memory wastage")
local instance2 = Foo();
-- however here, "Bar" function would be created again, which leads to increased memory usage
if ( instance2.Bar ~= instance1.Bar ) then
print( " - functions are not same!" );
end
print(" ");
But in general you don't want to do this. When you need reference to "self" (which is just standardized argument name) use either ":" operator or put self as first argument. Example
Code:
Foo = class();
Foo.Constructor = function( self )
self.secret = "123456789";
end
Foo.Bar = function( this )
-- now there wouldn't be any "self" but "this" instead (holding the same reference).. it is against the standard, but it can come in handy sometime
Turbine.Shell.WriteLine( this.secret );
end
function Foo:Smth()
Turbine.Shell.WriteLine( self.secret );
end
local instance = Foo();
instance:Bar();
instance.Bar( instance );
instance:Smth();
instance.Smth( instance );
|
10-08-2010, 10:06 AM
|
|
The Undying
|
|
Join Date: Sep 2010
Posts: 207
|
|
Okay, so you're basically saying that there's really no need to duplicate the function itself, and by sticking with just using "self" for variables, it would have the same effect, with less memory usage.
Do I have this right?
If so, one question. If you only create one instance of a class, and use:
self.Foo=function()
how many copies of that function are created? Would it just be one, or would there be two? This isn't some kind of justification either - I'm just curious how it would work. Obviously conserving memory should be a priority.
Of course, that does bring into question why Turbine chooses to do events eg:
Foo.MouseClick = function (sender,args)
with the dot operator instead of the colon.
|
10-08-2010, 11:07 AM
|
The Undefeated
|
|
Join Date: Oct 2010
Posts: 8
|
|
If that class would be singleton (only one instance across whole application), then it would create only one function. However it is still better to use self.Foo = function( self ) so you don't have to remember whether you must call it with "." or ":" and always call every function on instance with ":". Also I'm not saying that you shouldn't create functions with "." operator, I'm talking about creating functions inside of function (which is not always bad.. sometimes it's neccessary, but if you don't have to then don't do it) and using always "self" when that function is going to be member of class.
Code:
Foo = class();
Foo.Constructor = function( self )
end
Foo.Bar = function( self, arg )
end
is exactly the same as
Code:
Foo = class();
function Foo:Constructor = function()
end
function Foo:Bar( arg )
end
and would work same as
Code:
Foo = class();
function Foo:Constructor()
self.Bar = function( self, arg )
end
end
or
Code:
Foo = class();
function Foo:Constructor()
function self:Bar( arg )
end
end
but the last 2 would eat more memory than the first 2 after creating more than 1 instance. The difference is small, but when you put 10000 small things together, they make one big thing so it's good to know how things work (try this http://www.lua.org/pil/16.html#ObjectSec )
Events are different thing. You need to do it like this, because you are changing function on another instance - so function foo:MouseClick( args ) would work too, just "sender" would be named "self" and you would loose "self" reference from parent scope.
Also note that lua has no support for events, so what we call events are just plain functions.
Last edited by Kryso : 10-08-2010 at 11:22 AM.
|
10-08-2010, 11:59 AM
|
|
The Undying
|
|
Join Date: Sep 2010
Posts: 207
|
|
Quote:
Originally Posted by Kryso
If that class would be singleton (only one instance across whole application), then it would create only one function. However it is still better to use self.Foo = function( self ) so you don't have to remember whether you must call it with "." or ":" and always call every function on instance with ":". Also I'm not saying that you shouldn't create functions with "." operator, I'm talking about creating functions inside of function (which is not always bad.. sometimes it's neccessary, but if you don't have to then don't do it) and using always "self" when that function is going to be member of class.
Code:
Foo = class();
Foo.Constructor = function( self )
end
Foo.Bar = function( self, arg )
end
is exactly the same as
Code:
Foo = class();
function Foo:Constructor = function()
end
function Foo:Bar( arg )
end
and would work same as
Code:
Foo = class();
function Foo:Constructor()
self.Bar = function( self, arg )
end
end
or
Code:
Foo = class();
function Foo:Constructor()
function self:Bar( arg )
end
end
but the last 2 would eat more memory than the first 2 after creating more than 1 instance. The difference is small, but when you put 10000 small things together, they make one big thing so it's good to know how things work (try this http://www.lua.org/pil/16.html#ObjectSec )
Events are different thing. You need to do it like this, because you are changing function on another instance - so function foo:MouseClick( args ) would work too, just "sender" would be named "self" and you would loose "self" reference from parent scope.
Also note that lua has no support for events, so what we call events are just plain functions.
|
Okay, well that makes sense. Thanks for the explanation
|
10-08-2010, 06:14 PM
|
|
The Undying
|
|
Join Date: Sep 2010
Location: Idaho
Posts: 40
|
|
Okay, that was a whole lot more exposition then I was expecting, but that is really good information! Defiantly something to remember!
As mentioned before, the “events” that Turbine built into the API, would it be better practice to assign them inside the class Constructor, or outside with the rest of the functions?
And On the topic of memory usage how likely are we going to run into a problem with our plugins eating too much memory? I know Digital Utopia has a very specific instance that it can and probably will have a problem, but what about the rest of us not working with image files? Because, yea, if it is likely I think my next "release" will be mostly just memory management cleanup.
|
10-08-2010, 07:23 PM
|
The Undefeated
|
|
Join Date: Oct 2010
Posts: 8
|
|
As I posted earlier - it's really small thing. You probably won't run into problems with too high memory usage, unless you work with tons of images that are currently bugged. I personally prefer to create events within constructor because of "self" reference from parent scope. There won't be any problems with memory, until you create new function many times per second or create enormous amount of instances - but you generally want to keep your application as clean as you can. Also I don't know how often lotro client collects garbage in lua - in wow it was once a few minutes, so even non-statistical addons could easily eat megabytes of memory sometimes. Good example would be some bufftimer - if you are going to create new object with buttload new functions on every update and not cache anything you will eat huge amount of memory in some situations. Also creating function doesn't eat only memory, but also processor time, but again it's really small thing.
To sum it up, feel free use all features of language even at expense of some performance, but be smart. Good trade is when you get something back from the performance loss, bad thing is throw performance away and not get anything from it. As example take this: http://forums.lotro.com/showthread.p...proved-classes
Sure, there is some performance loss because of function call on __index and __newindex but hey, I won't spend hours of debugging because I forgot to call base constructor ever again ;p
|
10-08-2010, 07:44 PM
|
|
The Undying
|
|
Join Date: Sep 2010
Location: Idaho
Posts: 40
|
|
I wont look too deep into my memory management then, maybe a little next time I do a code cleanup.
|
Posting Rules
|
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts
HTML code is Off
|
|
|
All times are GMT -5. The time now is 01:17 PM.
|
|