Page 8 of 25

Re: middleclass & middleclass-extras: Object Orientation for

Posted: Sun Oct 24, 2010 11:14 pm
by kikito
Robin wrote:With the most recent GitHub version, :include() returns nil.
It should not

Try executing git submodule update.
Robin wrote:EDIT: also, there doesn't seem to be a :getCurrentState() function or the like, but I want use states for callbacks. Is there a non-hackish solution for this?
That is normally not the way you should use states. There's a method called getCurrentStateName (returns a string).

What do you intend to do, exactly?

Re: middleclass & middleclass-extras: Object Orientation for

Posted: Mon Oct 25, 2010 7:46 am
by Robin
kikito wrote:
Robin wrote:With the most recent GitHub version, :include() returns nil.
It should not

Try executing git submodule update.
Oh, it turns out I wasn't as up to date as I thought after all. Sorry. :oops:
kikito wrote:That is normally not the way you should use states. There's a method called getCurrentStateName (returns a string).

What do you intend to do, exactly?
I know about getCurrentStateName, but I want the game state to contain functions to which the love callback would refer.

Re: middleclass & middleclass-extras: Object Orientation for

Posted: Mon Oct 25, 2010 9:13 am
by kikito
Robin wrote:I know about getCurrentStateName, but I want the game state to contain functions to which the love callback would refer.
I gathered that you wanted to do something like that. But I don't understand why. I mean, if you need a reference to the method 'foo' when the game is in state 'State' inside love.update, you could do this:

Code: Select all

function love.update(dt)
  game:foo() -- the State:foo function will be called when game is on the state 'State'
end
But you probably already knew this. Why is it that you can't use this pattern?

Re: middleclass & middleclass-extras: Object Orientation for

Posted: Mon Oct 25, 2010 9:41 am
by Robin
:shock:

I sure feel dumb now.

Re: middleclass & middleclass-extras: Object Orientation for

Posted: Mon Oct 25, 2010 10:19 am
by kikito
:nyu:

What are you doing, by the way? Any chance of seeing it somewhere?

Re: middleclass & middleclass-extras: Object Orientation for

Posted: Mon Oct 25, 2010 11:22 am
by Robin
kikito wrote:What are you doing, by the way?
A game.
kikito wrote:Any chance of seeing it somewhere?
Oh, you will. Just not yet. ;)

Re: middleclass & middleclass-extras: Object Orientation for

Posted: Tue Oct 26, 2010 11:04 pm
by kikito
Small update.

Robin pointed up that I was doing something stupid with the git submodules inside middleclass-extras (the middleclass repository url was not the 'public' one, so it worked allright on my machine but no one else could get it correctly - duh). That is fixed now.

I've also just finished doing a change to the Callbacks module.

This one has been by far the most difficult one to nail, but it is reasonably working now so I wanted to share it with the world.

The callbacks module allows you to 'hook' functions to existing functions.

Quick look:

Code: Select all

Human = class('Human'):include(Callbacks)
function Human:brushTeeth() print('Brushing teeth') end
function Human:sleep() print('ZZZ') end
Human:addCallback('before', 'sleep', 'brushTeeth') -- the last param can also be a function

local peter = Human:new()
peter:sleep() -- normal call, prints ZZZ
peter:sleepWithCallbacks() -- prints 'Brushing teeth' and then 'ZZZ'
I hope it is easy enough to understand. Now the nice part: it works ok with inheritance:

Code: Select all

Soldier = class('Soldier', Human) -- subclass of human
function Soldier:haveNightmare() print('Having nightmare') end
Soldier:addCallback('before', 'sleep', function(soldier) print('Putting gun under pillow') end) -- add another callback before
Soldier:addCallback('after', 'sleep', 'haveNightmare') -- and also one callback after
local huck = Soldier:new()
huck:sleepWithCallbacks() -- prints 'Putting gun under pillow', 'Brushing teeth', 'ZZZ', 'Having nightmare'
Callbacks can also be used to interrupt or cancel the execution of a method: any callback returning false will stop the execution of the 'xxxWithCallbacks' method and immediately return false.

Also, initialize and destroy are special: they are always executed with callbacks. This is very useful for implementing fun stuff; the Apply module, for example, uses the Callbacks module to 'register' and 'unregister' items from the 'class list' automatically, without the user having to worry about it. Which is cool.

So yeah, last weekend was supposed to be the 'start pumping up documentation' weekend, but at the end I decided to continue doing this fun stuff. :nyu:

I'm even considering making another change - getting rid of the xxxWithCallbacks and adding a xxxWithoutCallbacks instead. Which option do you prefer?

Re: middleclass & middleclass-extras: Object Orientation for

Posted: Tue Oct 26, 2010 11:47 pm
by TechnoCat
kikito wrote:

Code: Select all

Human = class('Human'):include(Callbacks)
function Human:brushTeeth() print('Brushing teeth') end
function Human:sleep() print('ZZZ') end
Human:addCallback('before', 'sleep', 'brushTeeth') -- the last param can also be a function

local peter = Human:new()
peter:sleep() -- normal call, prints ZZZ
peter:sleepWithCallbacks() -- prints 'Brushing teeth' and then 'ZZZ'
I hope it is easy enough to understand. Now the nice part: it works ok with inheritance:
Took me quite a few reads to figure out what was going on. The fact everything being passed in was a string was throwing me off.

Code: Select all

Human:addCallback('before', 'sleep', 'brushTeeth')
param1: string. when the callback is called. before/after
param2: function pointer. the function that is calling the callback.
param3: function pointer. the callback.
kikito wrote:Also, initialize and destroy are special: they are always executed with callbacks. This is very useful for implementing fun stuff; the Apply module, for example, uses the Callbacks module to 'register' and 'unregister' items from the 'class list' automatically, without the user having to worry about it. Which is cool.
I still don't get this though.
kikito wrote:I'm even considering making another change - getting rid of the xxxWithCallbacks and adding a xxxWithoutCallbacks instead. Which option do you prefer?
I think xxxWithoutCallbacks is more useful. More useful because I believe it would be less used that is.

Re: middleclass & middleclass-extras: Object Orientation for

Posted: Wed Oct 27, 2010 6:53 am
by kikito
TechnoCat wrote: Took me quite a few reads to figure out what was going on. The fact everything being passed in was a string was throwing me off.

Code: Select all

Human:addCallback('before', 'sleep', 'brushTeeth')
param1: string. when the callback is called. before/after
param2: function pointer. the function that is calling the callback.
param3: function pointer. the callback.
param1 must be either the string 'before' or the string 'after'. Can't be anything else.
param2 is a method name. It can exist, or it can be a not (yet) existing method.
param3 is a callback, but can be either a method name ("pointer" as you called it) or a normal lua function (first param must be the instance -'self')
TechnoCat wrote:
kikito wrote:Also, initialize and destroy are special: they are always executed with callbacks. This is very useful for implementing fun stuff; the Apply module, for example, uses the Callbacks module to 'register' and 'unregister' items from the 'class list' automatically, without the user having to worry about it. Which is cool.
I still don't get this though.
This means that there's no 'initializeWithCallbacks' and 'destroyWithCallbacks' - destroy and initialize always use their callbacks. This is useful for making mixins because very often you need mixins to do stuff with your objects, independently from the user. The Apply mixin allows a class to 'invoke a method in all its instances'. So if you want to invoke the 'pause' method in all the instances of the class Goblin, you do something like this:

Code: Select all

 Goblin = class('Goblin'):extends(Apply)
local goblin1, goblin2, goblin3 = Goblin(), Goblin(), Goblin() -- create 3 goblins
-- invoke 'sleep' in all instances of goblin:
Goblin:apply('sleep') 
In order to do that, you have to store references to all instances of each class on some 'instance list'. Without callbacks, the lib would have to provide means for the initializers/destroyers to manually 'register' and 'unregister' each instance. In other words, the lib user would be forced to invoke something like this:

Code: Select all

function Goblin:initialize()
  super.initialize(self)
  self:registerInClassList() -- provided by apply
end
function Goblin:destroy()
  super.destroy(self)
  self:unregisterFromClassList() -- provided by apply
end
By making initialize and destroy 'special' for the callbacks module, this is done 'transparently'; the Apply module calls the register / unregister methods transparently. So it gets simpler and less error-prone.
TechnoCat wrote:
kikito wrote:I'm even considering making another change - getting rid of the xxxWithCallbacks and adding a xxxWithoutCallbacks instead. Which option do you prefer?
I think xxxWithoutCallbacks is more useful. More useful because I believe it would be less used that is.
Yeah, me too. The xxxWithCallbacks was simpler to implement :3 But I'll give it a try, because the xxxWithoutCallbacks sounds even more 'transparent'; and most importantly, 'initialize' and 'destroy' could stop being 'special'.

Re: middleclass & middleclass-extras: Object Orientation for

Posted: Wed Oct 27, 2010 11:25 pm
by Robin
Very interesting.
kikito wrote:Yeah, me too. The xxxWithCallbacks was simpler to implement :3 But I'll give it a try, because the xxxWithoutCallbacks sounds even more 'transparent'; and most importantly, 'initialize' and 'destroy' could stop being 'special'.
Yes, a very good idea. Although I'm wondering if there's a cleaner way to do it. Having to call xxxWith(out)Callbacks seems a bit hackish.