Using Self as a Method Default

BlitzMax Forums/BlitzMax Programming/Using Self as a Method Default

Chroma(Posted 2007) [#1]
Here's what I'm trying to do. I want to use self as a method default like so.

Method Sample(a:blah, b:blah = self)
     self.ni = b.n + a.n
End Method

I get a "Self can only be inside a Method" error. Is there any way to make a default the main type being used?


grable(Posted 2007) [#2]
No.

And why would you need to? its allready itself ;)

Id rather use Null and test for that instead.


Gabriel(Posted 2007) [#3]
Method Sample(a:blah, b:blah = Null)
    If b=Null Then b=Self
    self.ni = b.n + a.n
End Method

?


H&K(Posted 2007) [#4]
Also, on a similar note, the Defalt has to be a constat anyway doesnt it? So even if you could use self outside the main body, it would fail, cos self isnt Constant.
You need to use code like Gabs if you want to set a global as default.


Chroma(Posted 2007) [#5]
Gabriel and grable: I'm already using null and an IF statement in that manner. There's a slight hit in performance and while it's not that big of a hit, I think being able to put self as a default would speed it up and be worth it.

If I can find someway to be able to make a default the self then I can eliminate the IF statement.

EDIT: H&K: I've already been doing what Gabriel posted since the beginning. And yes, according to the docs a default has to be a constant.

But since it has to be a constant I guess this is a dead issue unless there's some undocumented magic way to do it.


tonyg(Posted 2007) [#6]
There's a slight hit in performance
errrrr... how slight?
<edit> and how do you know?


H&K(Posted 2007) [#7]
I'm already using null and an IF statement in that manner. There's a slight hit in performance and while it's not that big of a hit

Well...... call me stupid if you want, but.. for you to have thought that self would work, then you must be calling the method from/by some other method/object of that type, so why dont you just pass self as a parrameter?

But to be honest I wouldnt worry about the hit, less you are calling it millions of times. And in that case I would write two different ones.


grable(Posted 2007) [#8]
But to be honest I wouldnt worry about the hit, less you are calling it millions of times. And in that case I would write two different ones.


And in that case the function call overhead is way more than a single If ;)


ImaginaryHuman(Posted 2007) [#9]
Can you say param=MyGlobal (a global variable) and set a Global to the Self of that object e.g. MyGlobal=MyType, so that if you don't pass something to the method it uses the global which happens to be the self of that object?


H&K(Posted 2007) [#10]
@Angel,

I dont think you can. My experiance has been that the Param has to be a constant

And in that case the function call overhead is way more than a single If ;)
Yep, but then we should all use gotos all the time ;)
My point being that if you had two different methods, one where you were passing a param, and one where you werent, which you selected at write time, then it would be faster than a function call and an if.


SculptureOfSoul(Posted 2007) [#11]
Here's a method. Not sure if it is any faster, though

 
Method Sample(a:blah, b:blah = null)
     self.ni = (self.n * Not(b)) + b.n + a.n
End Method



Chroma(Posted 2007) [#12]
True H&K, but it's all apples and oranges. You can have 2 separate methods or you can have an if statement and take a small hit that you wouldn't notice anyways.

Here's the full experiment. As most of you know I've written/translated a math/vector lib over the years. They've been used a lot by quite a few people and have shown up in many demos. There is one thing I want to point out to Josh though but I'll get to that in a bit.

I'm taking each math method and stuffing it inside a bare bones loop.


While Not KeyHit(KEY_ESCAPE)
Cls

For a = 1 to 1000000
     v1.Add( v1, v2 )
Next

'FPS
Show_FPS()

Flip 0
Wend


I do this with each method and do the internal works a few different ways, keeping the fastest iteration.

If I do a straight:
Method Add(a:TVector3)
     self.x :+ a.x
     self.y :+ a.y
     self.z :+ a.z
End Method


I get an FPS of 57 (very slow machine btw). And then I use this as a baseline.

Now, if I do a:
Method Add(a:TVector3, b:TVector3 = Null)
     If b = Null Then b = Self
     self.x = b.x + a.x
     self.y = b.y + a.y
     self.z = b.z + a.z
End Method

This way gets 55 fps but allows me to write it either v1.add( v2 ) or v1.add( v2, v3 ). There's a little more versatility there. And I ponder, is the tiny bit more versatility worth 2 fps when cycling it 1 million times? Might seem very trivial to you guys, but I'm writing my final math lib here. It's in the archives and growing almost every day. So now you know what exactly I was trying to do.

If I could set the 2nd argument in the method to self, then I could avoid the 2 fps hit and have the versatility also. ie. Have my cake and eat it too.

To Josh/Halo/Leadwerks: The original BMax math lib I put into the archives is SLOW. Creating a new vector type for each method was a bad call on my part and there's a significant hit with it.

Bad way:
Method Add:Vector( v:Vector )
     Local res:Vector = New Vector
     res.x = self.x + v.x 
     res.y = self.y + v.y
     res.z = self.z + v.z
     Return res
End Method


Best way:
Method Add(v1:Vector, v2:Vector)
     self.x = v1.x + v2.x
     self.y = v1.x + v2.y
     self.z = v1.x + v2.z
End Method

or
Method Add(a:TVector3)
     self.x :+ a.x
     self.y :+ a.y
     self.z :+ a.z
End Method

Doing it the way above yields about a 400% increase in speed.

The bad way above also showed up in a TQuaternion part of MiniB3D if I'm not mistaken. Changing it to the Best Way should speed things up immensely.


H&K(Posted 2007) [#13]
Bad way:

Method Add(v:Vector)
     Local res:Vector = New Vector
     res.x :+ v.x
     res.y :+ v.y
     rex.z :+ v.z
     Return res
End Method
You do realise that youare not actuly adding anyhting here dont you? And you cannot return anything
Method Add(V:Vector)
    Self.x:+ V.x
    Self.y:+ V.y
    Self.Z:+ V.z
Endmethod


PS. Im not saying do it like this, just that thats how I do it.
I have
SM_Add and RM_Add (Because I do have two differnt ones, depending on if I am adding a vector to self, or adding the vector and self and passing the result to somthing else


Chroma(Posted 2007) [#14]
H&K: That was a typo and it's fixed now.

In your 2nd method there's no reason to have return self in your method. I guess if you're passing it on to something else you could just use the method on the vector you want to pass it to. passto.add( v1, v2) or v1.add( v2 ). If you had an IF statement you could use 1 function and write it either way.


ImaginaryHuman(Posted 2007) [#15]
If you want to avoid `if's`, sometimes a function pointer is a good way as it takes you directly to the code that you want.


Chroma(Posted 2007) [#16]
Hmm...if there was someway to point to the self from the method line without and special pre calc set up. It's beyond me.


H&K(Posted 2007) [#17]
If your 2nd method there's no reason to have return self in your method. I guess if you're passing it on to something else you could just use the method on the vector you want to pass it to
hahah

We actualy had quite a long thread about this. And the conclution was 1) The speed difference was very small. 2)It was faster to return self but not allocate it, than not to return anything.

If you had an IF statement you could use 1 function and write it either way.
Well yes ;) that has been the point of my posts.
My general rule is, if its not in the game loop make the methods as general as possible, if it is in the game loop make them as specific as possible.

And yes,
V1.SM_add(V2)
V3=V1.Rm_add(v2)


Chroma(Posted 2007) [#18]
If you want to avoid `if's`, sometimes a function pointer is a good way as it takes you directly to the code that you want.


AFAIK you can only use ptr with an int? I tried using a Ptr to my type but I get Illegal Pointer Type error.


H&K(Posted 2007) [#19]
you can only use ptr with an int

For example
Field F_BeatFunction(Me:TBeat,event:TEvent)

Is a field pointer to a function that takes a beat and an event as paramaters


Chroma(Posted 2007) [#20]
H&K: Can you use a pointer as a method default to point to the invoked type?


Chroma(Posted 2007) [#21]
Here's something that's weird.

The fastest method gets 57 fps (in a 1 million cycle loop with Flip 0).

If I do it this way (notice the 'this' field):
Type TVector3
     Field x:Double, y:Double, z:Double
     Field this:TVector3

     Function Create:TVector3(x:Double = 0, y:Double = 0, z:Double = 0)
          Local v:TVector3 = New TVector3
          v.this = v
          v.x = x
          v.y = y
          v.z = z
          Return v
     End Function

     Method Add(a:TVector3, b:TVector3 = Null)
          If b = Null Then b = self.this
          self.x = b.x + a.x
          self.y = b.y + a.y
          self.z = b.z + a.z
     End Method
End Type

I get 56 fps which isn't a bad hit at all and lets me write it both ways of v1.add( v2 ) and v1.add( v2, v3). For some reason, setting b to a field that's holding the type is faster than just setting b to self. /shrug


Gabriel(Posted 2007) [#22]
Gabriel and grable: I'm already using null and an IF statement in that manner. There's a slight hit in performance and while it's not that big of a hit, I think being able to put self as a default would speed it up and be worth it.

Not that big of a hit? I tried running the function with and without an if 5 milliion times and they're both showing identical times ( to the nearest millisecond, which is all I can be bothered to code ) so I'm struggling to see the hit myself.


Chroma(Posted 2007) [#23]
so I'm struggling to see the hit myself


In my eyes, that's a good thing. :)


ImaginaryHuman(Posted 2007) [#24]
Self might require indirectly accessing such as myinstances.mytype rather than if a field contains the self it would just be mytype. ??

I'm not sure I still understand why you want to pass self to a method, when methods can only ever refer to one self. Is it just so you can pass some other self to it from a different instance? Why would that instance not be using its own methods?


Chroma(Posted 2007) [#25]
Originally I wanted to do this:
Method Add(a:TVector3, b:TVector3 = Self)
     self.x = b.x + a.x
     self.y = b.y + a.y
     self.z = b.z + a.z
End Method

So I could eliminate an IF statement. B would default to self if no vector was passed to it. But I can't do it and the IF statement is a neglibible speed hit anyhow.


SculptureOfSoul(Posted 2007) [#26]
If you are curious about speed, try this, the method I posted above.

I have to admit that I'm curious if it's any faster than an if myself

...reposting:
Method Sample(a:blah, b:blah = null)
     self.ni = (self.n * Not(b)) + b.n + a.n
End Method


If b isn't passed in, the expression evaluates to
self.ni = self.n * 1 + 0 + a.n

if b is passed in it evaluates to
self.ni = self.n * 0 + b.n + a.n


Chroma(Posted 2007) [#27]
Testing now...brb.

EDIT: Ouch! That cut the speed by 50%. Very innovative attack to the problem though.


H&K(Posted 2007) [#28]
Again, if you are worried about speed, just have two different methods that you select at write time.
It seems stupid to me to spend all this effort when as you type in the code you know if you are passing a parameter or not. And if you are not then select the method that takes one parameter and uses self. If so, then use the method that takes two parameters.
(And if you are really worried that some times the paramater that you pass might be NULL and you want to use the first method, then right a third one that choses between the two, which you only call in that very rare situation)

New basic rule, if you are worried about the speed of an If and IFing at write time is possible, then IF at write time. You are a lot better off with slightly larger code.


Chroma(Posted 2007) [#29]
It seems stupid to me to spend all this effort


It's really no effort. And my original question was answered. You can't put a self as a method default. At this point we're just having fun. /shrug


SculptureOfSoul(Posted 2007) [#30]
New basic rule, if you are worried about the speed of an If and IFing at write time is possible, then IF at write time. You are a lot better off with slightly larger code.



This is where something like Lisps macro's would be nice. Basically, the macro would be evaluated at compile-time and could expand into either of the two separate calls based on if the parameter or present or not. I.E, you get the speed of "IF'ing at write time", while you get the coherence of a single type of function call.