Abstract method, what am I doing wrong?

BlitzMax Forums/BlitzMax Programming/Abstract method, what am I doing wrong?

Yue(Posted April) [#1]



grable(Posted April) [#2]
The types of overriding methods must match. In your case there is a mismatch on the return type between NO type and TSol.
The only thing that can change when overriding a method is the Return Type, and ONLY if they are both Objects.
Just use :Object or :TLuz on the base method and it should work as expected.


Yue(Posted April) [#3]
@grable

Thanks You. :)


' Definición de Tipo.
Type TLuz 

	' Propiedades.
	Field tipo:Byte 
	Field color:Byte[3]  ' Color RGB.
	Field rango:Int
	Field anguloCono:Int[1]
	
	' Luz.
	Field luz:Int 



	Function Init:Object(tipo:Byte = LIGHT_DIRECTIONAL   )  Abstract

	

  End Type 

' Luz Direccional. ( Sol )
Type TSol Extends TLuz


	
	Function Init:TSol(tipo:Byte = LIGHT_DIRECTIONAL)
	
	
		Local sol:TSol = New TSol
	
	

			Sol.luz:Int = xCreateLight( sol.tipo:Byte)
	
		Return ( sol:TSol ) 
	
	End Function
   
	

	



End Type

' Objetos.
Local sol:TLuz = Null 




gpete(Posted April) [#4]
in your code sample one says "tipol" and the other says "tipo"


Derron(Posted April) [#5]
Avoid spelling mistakes by adding "strict" on top of the project source code.
(I prefer "Superstrict").


Also it is better to have
Function Init:TLuz(tipy:Byte = LIGHT_DIRECTIONAL ) Abstract

And (imho) even better is, to have an "init" being a method instead of a function. You do not "create" something, but _inititialize_ an existing code


Ok, so to the first, why ":TLuz"? Returning the base object type "object" will make it harder to easily access properties of "TLuz". Returning "TLuz" allows to directly call another Method of this object, or accessing a property.

So with ":TLuz" you could do this (assuming it has this Method "SetToDeathStar()":

local myPlanet:TSol = new TSol.Init(LIGHT_DIRECTIONAL).SetToDeathStar()

Above is called "Method chaining". Because you returned "TLuz" (or ...even TSol) you are allowed to call a method or function of "TLuz". With returned Objects you need to cast them first:
local myPlanet:TSol = TSol(new TSol.Init(LIGHT_DIRECTIONAL)).SetToDeathStar()


As "TLuz" is compatible to "object" you can add the returned "TLuz" instance to everything accepting "object". But it is not possible to pass "object" to something awaiting a "TLuz", at least not without casting it to "TLuz(obj)" before.




Ok, so why "Method" instead of "Function"? As said you do an "init", not a "create". So typically you would have a method doing the initialization and returning "self" at the end


Method Init:TSol(tipo:Byte = LIGHT_DIRECTIONAL)
...
return self 'this is important!
End Method


BlitzMax often uses Functions for convenience. So a "DrawRect()" is a function wrapping around the method "DrawRect" of the renderer (or something in the likes... just an example).
So it is common to have this then:

'myfile.bmx

Function CreateLuz:TLuzl(tipo:Byte = LIGHT_DIRECTIONAL)
  return new TLuz.Init(tipo)
End Function

Function CreateSol:TSol(tipo:Byte = LIGHT_DIRECTIONAL)
  return new TSol.Init(tipo)
End Function


Type TLuz
...
  Method Init:TLuz(tipy:Byte = LIGHT_DIRECTIONAL)
    ...
    return self
  End Method
End Type

Type TSol extends TLuz
  'overriding the return-type with something compatible is allowed!
  Method Init:TSol(tipy:Byte = LIGHT_DIRECTIONAL)
    ...
    return self
  End Method
End Type


The important thing to learn here is: you are allowed to replace ":TLuz" with something like "TSol", as "TSol" does extend from "TLuz" - so they are hmm "compatible" (same for object->TLuz and object->TSol).



Above are just some hints, no need to change your code to follow these suggestions but maybe you learned something new.


bye
Ron


Yue(Posted April) [#6]
@Derron

Hi, Derron,
I always have something new to learn., what you advise me is that the builder has it out of the types? Where only within the types have only methods.

The translator does not help me much, spatially with the line of code.


local myPlanet:TSol = new TSol.Init(LIGHT_DIRECTIONAL).SetToDeathStar()



At the end before viewing this message, this is as follows.

A class called TIlumination (T Lighting) from where I create objects derived from a class TLuz (Light T).

I do not know if this is the best way to implement this from programming to objects.



Derron(Posted April) [#7]
Of course you are not needed to have the "createXYZ"-functions outside of a type. I just wrote them as BlitzMax does this very often.

As said there is "DrawImage" - which is such a "outside function" so you do not need to know the exact typename which is currently used.

You will also know it from Xors3D and the other engines. They Hide "TLamp.Create(...)" by using "CreateLamp(...)".


that long "local myPlanet ..."-line is just showing a way to chain methods together.

Imagine you have a type "TRectangle". Then you have methods "SetX(), SetY(), SetWidth(), SetHeight()" ...

If all of these methods return ":TRectangle" (means "return self" !!) you can do this:

myRectangle.SetX(10).SetWidth(100).SetHeight(x)


For a lamp this could look like this:
mLamp.SetIntensity(100).SetRGB(255,255,0)

of course you could still do a
myRectangle.SetX(10)
myRectangle.SetWidth(100)
...


But sometimes it looks a bit cleaner if you could do it "in one line".




Even more important: with default BlitzMax modules the "reflection code" (getting information of objects dynamically while running the application) you can only access "fields" and "methods" - so no globals, no "function".

This allows you to run a "Method Init:object()" via scripting/dynamically - which is not possible with "Function Init:object()".



bye
Ron


Yue(Posted April) [#8]
Thank you very much, in a way I can understand, and there are many things to learn. By the way the lists have methods, for example List.Clear (), List.AddLast ()


Derron(Posted April) [#9]
Yes, a list has "list.clear()" and so on - but there is also

ClearList(list)
ListAddLast(list, item)
[...]

And maybe even more often used
local list:TList = CreateList()
instead of
local list:TList = new TList


So such things are most often done for "convenience" but sometimes to hide the used implementation ("wrapper").
Imagine you have:
TDirectSoundEngine
TOpenALEngine
TFMODEngine

All of them have the Method "PlaySound(soundobject)".

Without a wrapper you will have this in your code:
TDirectSoundEngine.PlaySound(boomSfx)

What happens if you want to replace the sound engine with OpenAL?
You need to find all "TDirectSoundEngine.PlaySound()"-calls and replace them with "TopenALEngine.PlaySound()".

Now someone creates that fancy wrapper function "PlaySound(obj:object)" which calls the used sound engine's PlaySound()-method for you.

You do not have to know anylonger if directsound, openal, ... is used, you just call this generic "PlaySound()"-function which does the rest for you.


Of course this "helper" could be again hidden in a Type whose instance knows about the used engine and so on.
Or something like "GetSoundEngine().PlaySound()" - with "GetSoundEngine()" being a helper function returning the currently used instance of a sound engine.
This often means, you could also use it directly:
TSoundEngine.GetInstance().PlaySound(boomSFX)

The basic idea behind this is better explained in other threads already - but the idea is, that every sound engine provides a method to play a sound and that every engine has to use the same method name and parameters.

Similar to TLuz and TSol they "share" this method definition and therefor could replace each other. The developer/user then does not need to know about the internal workflow, just the commands to the outside world are of interest.
A globally available PlaySound() is then also existing to hide the existence of "object orientation" at all (BlitzMax tries to hide that to keep things easy - but of course allows to use objects and their methods for more advanced usage).



bye
Ron


Yue(Posted April) [#10]
Hi Derron, this is incredible, I finally understood, well I think.

I will make an example.


Local BoxRed:TBox  = CreateBox("Red")

Print BoxRed.colorBox

Function CreateBox:TBox(colorBox:String)


		Return New TBox.Init(colorBox:String ) 
	

End Function 



Type TBox


	Field colorBox:String

	Method Init:TBox(colorBox:String)
	
	
		Self.colorBox:String = colorBox:String
		
		
		Return Self
	
	End Method



End Type 





In that case I understand why list.Clear () exists and listClear (list).

This is great, thank you very much Derron. :)


Yue(Posted April) [#11]
New Code :)

File TCamera.bmx

' Comandos.
Function CreateCamera:TCamara()

	
	Return  New TCamara.Init()

End Function 

Function CameraClsColor( camara:TCamara, rojo:Byte, verde:Byte, azul:Byte ) 


	 camara.ColorCLS( camara:TCamara, rojo:Byte, verde:Byte, azul:Byte ) 

End Function 



' Definición de Tipo.
Type TCamara


	Field lente:Int 
	Field padre:Int
	
	Global Lista:TList

	Method Init:TCamara()
		
		Self.lente:Int  = xCreateCamera()
		
	
		Return  Self
	
	End Method
	
	Method ColorCls(  camara:TCamara, rojo:Byte, verde:Byte, azul:Byte ) 
	
		xCameraClsColor (  Camara.lente:Int, rojo:Byte, verde:Byte, azul:Byte )  
	
	End Method



End Type 


' Objetos.
Local camara:TCamara = Null 



File Start.bmx
camara:TCamara = CreateCamera()
CameraClsColor(Camara, 12, 183, 242 ) 




Nice!!


Derron(Posted April) [#12]
While the "function" is done right, you missed something in your "ColorCls"-Method...

	
	Method ColorCls(  camara:TCamara, rojo:Byte, verde:Byte, azul:Byte ) 
	
		xCameraClsColor (  Camara.lente:Int, rojo:Byte, verde:Byte, azul:Byte )  
	
	End Method


See that "camara:TCamara" parameter? When using a "method", you already have access to the parent (in this case, this is a "TCamara" already).

So you could access "lente" already very easily:
Function CameraClsColor( camara:TCamara, rojo:Byte, verde:Byte, azul:Byte ) 
	 camara.ColorCLS( rojo, verde, azul ) 
End Function 
	

Type TCamara
[...]
	Method ColorCls( rojo:Byte, verde:Byte, azul:Byte ) 
		xCameraClsColor( self.lente, rojo, verde, azul )  
	End Method


See that self it is used to say "use my 'lente'". You even could skip the "self." and just write "lente". It will work too.
BUT .. there is a but ;-) ... if you had a method "ColorCls( lente:int, rojo:Byte ...)" then Blitzmax would not know which "lente" you are talking about - "self.lente" or the param "lente". In that case you would need to write "self.lente". Means: it is good to use "self." to allow others to recognize faster what you are refering to (property of an object or global variable / parameter).



@ TCamara and "CameraClsColor"
You should try to use the same localization. So call it "TCamera" - or rename "CameraClsColor" to be something in the desired language too.
Why? Without "strict" or "superstrict" you will end up guessing how things were spelled - and once you misspell something a new local variable is created and trouble will happen.
So "consistence" will help very much.



PS: Good to read you feel like learning something from it.



bye
Ron


Yue(Posted April) [#13]
What is not now about creating such Commands in English or Spanish. XD.

My Question is, why do that, CreateCamera and not use xCreateCamera, I think my answer is because I can create some states as per isvisible (Camara) or GetRange (Camara).

How easy is it to create a module based on a functional library in Blitz3D to work with Xors3D in BlitzMax?


Derron(Posted April) [#14]
One of the benefits of your "CreateCamera" is, that it eases the pain if you once replace Xors3D with another engine.


Nonetheless I prefer the object oriented approach "camera.SetRoll(10)" instead of "SetCameraRoll(camera, 10)".


Why using your own "CreateCamera" at all? If you have your own "TCamera" object, you might do things differently to a "Xors3D-Camera".

I do not know whether Xors3D allows a camera to "look at" an object. So your camera might get the method "LookAtEntity(entity)" (or "item" or "object" ...). Then within that function you do the magical coding work so a xors-camera (wrapped in the TCamera of yours) is looking into the direction of the object.


What I mean with "wrapped in the TCamera" is what you already did: "lente:int" is holding the instance of the xors3d-camera alreary. So outside of "TCamera" nobody needs to know that you use a xors3d-camera. Everything they want to do with their "camera" is done via "TCamera". So if xors only accepted "CMYK" (cyan magenta yellow black-portion-aka-"key") your camera would help outsiders to set the color via "RGB" (red green blue).


As I wrote above - many things in the BlitzMax modules do it the same way. If you want to render with OpenGL, then you use some GL***-commands, if you want to use DirectX then the commands are named differently. The wrappers (Max2D in this case) hide all of these things from you. You do not have "TGLImageFrame" or so - you just use "TImage" without having to know what renderer you use.


@ module based ...
I do not know - weren't you satisfied enough with MiniB3D, Irrlicht3D, Ogre3D, ... or whatever is available for BlitzMax?


bye
Ron


Yue(Posted April) [#15]
Thank you very much, what happens is I was thinking about the system of physical bullet that I find very nice in Blitz3D. I have looked at the form of the Xors3D module and possibly thought that if I relied on that, I could take out the functions of the bookstore bullet and be able to use BlitzMax + Xors3D + Bullet.