LoTROInterface

LoTROInterface (https://www.lotrointerface.com/forums/index.php)
-   Lua Programming Help (L) (https://www.lotrointerface.com/forums/forumdisplay.php?f=50)
-   -   issue with functions inside of a class (https://www.lotrointerface.com/forums/showthread.php?t=1126)

SanDBoX 10-04-2010 10:16 PM

issue with functions inside of a class
 
I recently ran across an issue where when I am defining functions inside of a class I can no longer use the "self" identifier such as:
Code:

10  MyWindow = class( Turbine.UI.Window )
11
12  function MyWindow:Constructor()
13    Turbine.UI.Lotro.Window.Constructor( self ) --this part works fine
14    self.int = 5
15    ...
16  end
17
18  function MyWindow:BronkenFunction()
19    self.int = self.int + 1
20  end

Oversimplified but should give the right idea, in this kind of an instance I end up getting an "Attempt to index local self (a 'nil' value)" error, and the thing that bugs me the most is I have functions in other files that work just fine and they are written the same way (outside the constructor)

Any idea what it is that I am missing? This really has me lost tonight...

moebius92 10-04-2010 11:59 PM

When you call the BronkenFunction(), are you using . or :? I.e.,

myWindow = MyWindow();
myWindow.BronkenFunction();

vs.

myWindow = MyWindow();
myWindow:BronkenFunction();

Because you should be doing the second.

SanDBoX 10-05-2010 07:32 AM

Okay, that worked, and explains why it was so flacky and only did it when I clicked one button (all the other ways it was called worked fine). Guess I was more tired then I thought last night.

Thank you.

Digital_Utopia 10-05-2010 09:42 AM

Quote:

Originally Posted by SanDBoX (Post 5099)
Okay, that worked, and explains why it was so flacky and only did it when I clicked one button (all the other ways it was called worked fine). Guess I was more tired then I thought last night.

Thank you.

If you want to call a function with the common dot operator, create the function like so:

Code:

MyWindow.BronkenFunction = function()
    self.int = self.int + 1
end

Then you could call it with "MyWindow.BronkenFunction()"

Kryso 10-07-2010 02:25 PM

And this example would fail again, because there would be no "self" (or at least no valid self).

Code:

function Table:Foo( arg )
    self.bar= self.bar+ arg;
end

is just syntactic sugar for

Code:

Table.Foo = function( self, arg )
    self.bar = self.bar + arg;
end

and

Code:

Table:Foo( 1 );
is the same as

Code:

Table.Foo( Table, 1 );

Bonechip 10-07-2010 03:29 PM

What I read....
 
If the inverting-core acceptor deflects the complex chronotron-feedback analysis, try to provoke a coil-composition reflex and several quantum biosphere resonances, this will create a restricted isovolumic cochrane graviton-prediction, which ought to in fact dampen the polarizing maintenance-filament formulas. Then attempt a minimum abstract component-delay correction phase to input a reversible lucifugal primary ionization perimeter operation to cancel the celestial info-sphere greenhouse effect level-limits. As you are doing this, set in motion six homeostasis global-attractors from the constant chemical cybernetic-induction elliptical-beam, this will dislodge a krypton placebo-molecule from the kinetic synthesis-accelerator.

You guys are talking greek. LOL

SanDBoX 10-07-2010 04:49 PM

LOL not that bad bonechip ;)

Still seams funny to me that LUA has such a flexable syntax, I am usto very defined lines instead of the the whole "you can do it this way, or if you want this way, or this way... " and the fact that it all still works unless you make a dumb mistake is just Icing on the cake.

Digital_Utopia 10-07-2010 09:14 PM

Quote:

Originally Posted by Kryso (Post 5124)
And this example would fail again, because there would be no "self" (or at least no valid self).

Well..part of that is a bit of a typo. It should be:

Code:

self.BronkenFunction = function()
    self.int = self.int+1
end

Instead of using MyWindow.

Kryso 10-08-2010 04:15 AM

Quote:

Originally Posted by Digital_Utopia (Post 5128)
Well..part of that is a bit of a typo. It should be:

Code:

self.BronkenFunction = function()
    self.int = self.int+1
end

Instead of using MyWindow.

Yea this would work if you created the function in another function, so it would take self from parent scope - which is in a lot of cases bad practice, because it would create new function on every call. I mean it is usefull sometimes, but it shouldn't be used so we can call functions that are supposed to have "self" reference with dot operator.

Digital_Utopia 10-08-2010 06:37 AM

Quote:

Originally Posted by Kryso (Post 5129)
Yea this would work if you created the function in another function, so it would take self from parent scope - which is in a lot of cases bad practice, because it would create new function on every call. I mean it is usefull sometimes, but it shouldn't be used so we can call functions that are supposed to have "self" reference with dot operator.

Except most "class" definitions are technically functions. eg:

Code:

MyWindow = class( Turbine.UI.Window )
function MyWindow:Constructor()
      Turbine.UI.Lotro.Window.Constructor( self )



end

And, given that it isn't a local function, (i.e. something that will never be called from outside the class), wouldn't it make sense to make a unique copy of that function for every instance of the class?

Kryso 10-08-2010 07:36 AM

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.

Digital_Utopia 10-08-2010 08:28 AM

Quote:

Originally Posted by Kryso (Post 5132)
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?

Kryso 10-08-2010 09:49 AM

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 );


Digital_Utopia 10-08-2010 10:06 AM

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.

Kryso 10-08-2010 11:07 AM

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.

Digital_Utopia 10-08-2010 11:59 AM

Quote:

Originally Posted by Kryso (Post 5136)
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 :)

SanDBoX 10-08-2010 06:14 PM

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.

Kryso 10-08-2010 07:23 PM

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

SanDBoX 10-08-2010 07:44 PM

I wont look too deep into my memory management then, maybe a little next time I do a code cleanup.


All times are GMT -5. The time now is 01:12 AM.

vBulletin® - Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
© MMOUI