Do something when setting a type parameter?

BlitzMax Forums/BlitzMax Beginners Area/Do something when setting a type parameter?

sswift(Posted 2006) [#1]
I was wondering if it would be possible to do something like this:

Sprite.Order = 10

And have that cause the field Order to be set, and a method activated which uses that information to perform an action, such as placing the object in the correct location in a list.

The best I've been able to figure out is doing something like this:

Type Sprite
    Field Order
    Method SetOrder(NewOrder)
        Order = NewOrder
        ' Do stuff here
    End Method
End Type


It seems like it might be much more elegant to be able to have a method with the same name as a field and have that method be triggered after the field is altered or something.

Or maybe not. I dunno. But if there is an alternative, it would be nice to know about it.


VP(Posted 2006) [#2]
Hmm. My first instinct is that this is not a good idea in practise. It sounds useful, and may have some interesting applications but from a code readability point of view I can't see how this wouldn't do anything other than obfuscate.

Is Sprite.Order(10) so bad an alternative?

Or even Sprite.Order(SOME_MNEMONIC_CONST) ... ?

**EDIT** I guess I'd have to see what context this is in for it to make much sense. It's just that changing the default behaviour of something is a very delicate procedure. Easy to get wrong, hard to get right.


Dreamora(Posted 2006) [#3]
The most simple alternative and obvious version is overwriting the method compare and then call array.sort / list.sort :-)


sswift(Posted 2006) [#4]
That might make sorting the list easier Dreamora, but it does not enable me to do the task described above, which is to provide a good way to make sure the list is sorted each time the order of a sprite is changed.


I guess I'd have to see what context this is in for it to make much sense. It's just that changing the default behaviour of something is a very delicate procedure. Easy to get wrong, hard to get right.




What context?

I want to be able to do the following:

' Set ship order, and sort list of sprites so the list is in the correct order.
ShipSprite.Order = 10

' Get ship order.
Temp = ShiSprite.Order


The act of setting the field causes a method of the same name to be triggered.

The only problem I can see with this, is that either the field is changed before the method is executed, or it is changed after the method is executed. Either one might be the desired behavior.

The only way around this is to somehow pass the new value to the method, so that we can access the old value and the new value at the same time, and choose whether we even set the order field to be the new value or not.

In other words, Order would be a function, not a field. But it would be a special function. A function which you can assign a value. And that value is passed to the function via a special variable. Also, the function is capable of storing a value permanently. In that way it is like a field. That value could also be accessed in the function from a special variable. And when you call the function and do not assign a value to it, the function would know this. Perhaps NewValue = Null in this case. And as a result of this, the function woukd return StoredValue.

So:

A = Sprite.Order

Function executes method Order, returns value stored in method (StoredValue) that is not lost when the method stops executing.

Sprite.Order = A

Function executes method Order, passes value of A in special variable NewValue which can be accessed within the function. Function then has the option of setting StoredValue = NewValue, which would be the same as setting a field, or it has the option of doing this before, or after it performs some other operation given the new data.


Right now, the best I know of that you can do is this:

A = Sprite.Order
Sprite.Order(A)

You can't do:
Sprite.Order = A

Also, in the first two examples, you're not storing a value in a field or method called Order, you have to have a Method called Order, and a field called something else, like _Order so the method, which sorts the list when you change the order, has something to store the value in.

Now is this more obfusticated? Perhaps, on the type level, there would be some confusion about what the Method is doing if people don't know what a ... (damn, there's a word for a variable in a function which you declare and it sticks around after the function ends, but I just can't remember the name of it) ... is, but there also would not be a second variable with almost the same name as the method in the type to cause confusion. And the user interface outside the type for setting the value would be neater.


sswift(Posted 2006) [#5]
Aha, the word I'm looking for is "Static".


So:

Type
    Method Order:Int()
       Static Value:Int
       Value = NewValue 
       Return Value
    End Method
End Type


Where NewValue is a special variable passed to the function, much like Self, when you do the following:

Sprite.Order = 10

In this case, NewValue in the function would be 10.


And with that, the method above would be functionally equivalent to:


Type
Field Order:Int
End Type



Except that Value would have to be a special variable that always exists, and if the user does this:
A = Sprite.Order

Then NewValue would have to be set to Value, so that executing the method doesn't change Value to 0 before returning it.


I dunno, maybe this is a crazy idea, but it seems to work out. And I was just wondering if you could do it. But I don't think BlitzMax even has static values.


Chris C(Posted 2006) [#6]
if you are sorting *every* time order changes that could end up a slowwwwww game

have you considered only sorting when you need the list to be in order


Perturbatio(Posted 2006) [#7]
I dunno, maybe this is a crazy idea, but it seems to work out. And I was just wondering if you could do it. But I don't think BlitzMax even has static values.


globals inside a method or function behave like static variables.


sswift(Posted 2006) [#8]
ChrisC:
The list needs to be in order every frame, because it is the draw order.

It's not slow, because generally you set the order of a sprite only once when you first create it, and you don't create many sprites each frame.

Also, by "sorting the list" I really mean inserting the sprite at the correct position in an already sorted list.

By inserting each sprite into the correct location when it is created, the list is always sorted, and each insertion always takes a most, N operations where N is the number of sprites, and at best will take just one operation.

If one spawned thirty new sprites in a single frame for an explosion, and a hundred sprites already existed in the scene, at WORST, the number of comparisons that would have to be performed would be something like 3465, which is not going to affect your framerate at all. That assumes that all the explosions are placed at the end of the list.

Sorting the list "only when needed", which would be every frame, on the other hand means you have just inserted 30 objects into the list and must assume they're out of order and the whole list needs to be resorted. That means you have to compare EVERY object in the list with every other one to get them all in the right order, rather than just comparing 30 objects to the other hundred or so objects already in the list. That is probably going to be much slower.

Anyway I produced a game for Lego which used this exact method, and there were no speed issues with it. And that game had lots of robots walking about, and biuilding things and creating dust clouds and little recycle bins moving about while blocks spawned and fell... Lots of stuff going on. I don't know how many sprites were onscreen at once, but it was probably around a hundred at least, with a hundred more offscreen waiting to scroll onscreen as the tower rose into the sky.


sswift(Posted 2006) [#9]
Perturb:
Do they alkso behave like local variables only accessable within that function?


sswift(Posted 2006) [#10]
Hmm, it appears you have to use parentheses after your method name when assigning the return value of a method to an integer:

Type Sprite
	Method Order:Int(NewValue:Int=Null)
		Global Value:Int
		If NewValue <> Null Then Value = NewValue 
		Return Value
    End Method
End Type

ThisSprite:Sprite = New Sprite 
ThisSprite.Order(10)

A = ThisSprite.Order()
Print A

A = ThisSprite.Order()
Print A

ThisSprite.Order(0)
A = ThisSprite.Order()
Print A


This does not work:
A = ThisSprite.Order

Error:
Unable to convert Int(Int) to Int

I think what it is doing is trying to assign a function/method pointer to A if I don't add the parentheses.

The second problem is that apparently, as far as BlitzMax is concerned, 0 and Null are the same thing. So I cannot use the lack of a value being passed to a function to trigger a different behavior.

Also, it's looking like this is just making the behavior of the code more confusing. It doesn't matter how elegant the solution is if people coming upon it for the first time have a hard time wrapping their heads around what is going on.


Tiger(Posted 2006) [#11]
Try this then,
Type Sprite
	Method Order:Int(NewValue:Int=-1)
		Global value:Int
		If NewValue <> -1 Then value = NewValue 
		Return value
    End Method
End Type

ThisSprite:Sprite = New Sprite 
ThisSprite.Order(10)

A = ThisSprite.Order()
Print A

A = ThisSprite.Order()
Print A

ThisSprite.Order(0)
A = ThisSprite.Order()
Print A



sswift(Posted 2006) [#12]
Tiger:
Yeah, I know you can use a special value to get around the null problem, but you have to choose your value carefully for each case to make sure it doesn't conflict with the functioning of the method.

In this case, -1 would be a bad choice, because Order can be negative. $FFFFFFFF on the other hand would be fine, because there's no way anyone's gonna use an order of more than plus or minus 20 or so.

Not really working out to be as nice an implementation as I hoped. I think this idea might be better suited for the garbage bin. :-)


Perturbatio(Posted 2006) [#13]
I would really like to see Set and Get implemented (as in delphi read/write).

i.e.
Type Sprite
	Field Order:Int Set SetOrder Get GetOrder
	
	Method SetOrder(val:Int)
		Order = val
	End Method

	Method GetOrder:Int()
		Return Order
	End Method
End Type




sswift(Posted 2006) [#14]
Perturbatio:
If I understand you correctly, what you're saying is that Delphi executes those methods when you set or get the Order field?

If so, that's pretty damn close to what I was asking for, and is fairly clear, and clean, and would suit my needs just fine.

So my idea wasn't all that crazy after all. :-)


Perturbatio(Posted 2006) [#15]
yes, that's what Delphi does. It's very handy for range checking, updating other linked/derived fields, etc.

*EDIT*
actually, If I remember correctly, they're called Read and Write in Delphi, but the exact same action occurs.