Help with Type Extends

BlitzMax Forums/BlitzMax Programming/Help with Type Extends

col(Posted 2009) [#1]
Hi all.

My problem is.........

I have a Type called Primitive. This type has some generic methods, for eg a Draw method an Intersect Method etc and some fieds plus more fields.
I then create a circle type that extends the Primitive type. The Circle type then has extra Methods like SetRadius for example. I also have a Plane Type with a SetSize Method. i want to store all Primitives in an array so that i can easily move through the array calling a generic Method in the Primitive Type.
My problem comes when creating the array. I can initialize the array with Prim:Primitive[5] for example, the initialize say slot [0] with a sphere with Prim[0]:Sphere then id have say Prim[1]:Plane for example. Sphere and Plane Types both extend from the Primitive Type. This works but the Methods in Sphere arent accessible. A code snippet may help with what im trying to explain..........

Type Primitive
	
	Field PrimitiveType:Int
	Field Name:String
	Field mat:material
	Field dir:vector
	
	Method New()
		mat:material=New material
		dir:vector=New vector
	EndMethod
	
	Method Intersect:Int(r:Ray, dist:Double)
	EndMethod

            Method SetMaterial(m:material)
		mat=m
	EndMethod
EndType

Type sphere Extends primitive
	Field centre:vector
	Field radius:Double
	Field rRadius:Double
	Field sqradius:Double
	
	Method GetType:Int()
		Return PRIMITIVE_SPHERE
	EndMethod
	
	Method Set(x:Double,y:Double,z:Double,r:Double)
		centre.x = x
		centre.y = y
		centre.z = z
		radius = r
		sqradius = r * r
		rradius = 1.0 / radius
	EndMethod

	Method New()
		centre:vector=New vector
	EndMethod
	
	Method Intersect ( r:ray , dis:Double)
	EndMethod
EndMethod

Global Prim:Primtive[5]
prim[0]=New sphere
prim[0].Set(128,128,50,30)
prim[0].mat.SetColour(255,128,0,1)




The code will compile and run if the prim[0].Set......... ISNT there even though the prim[0].mat.SetColour...... IS.

Its like the code is only initializing the base Primtive Type and NOT the Sphere Type. I know the Sphere Extends from the Primitive and the array is of Primitives but if the code compiles and runs with prim[0]=New Sphere then i'm expecting the Methods in Sphere to be available.
Am i doing something wrong here ?? Please tell me its not a bug !!

many thanks


ziggy(Posted 2009) [#2]
You may have to cast the item in the array to the sphere again.

prim[0]=New sphere
Sphere(prim[0]).Set(128,128,50,30)
prim[0].mat.SetColour(255,128,0,1)


Otherwise, when doing this prim[0], the compiler will try to access the object as defined in the prim declaration, not in the item declaration.

latebinding, wich would mean that the compiler checks each item, to determine the type, and then accept speciffic calls is SLOW and not performed in BlitzMax (as oposite to other slow languages like Visual Basic). So recasting is something you'll have to do in code.


REDi(Posted 2009) [#3]
Its not a bug :)

You'll need to cast the objects back to their original types so that BlitzMax knows what to do with them, something like this...
Local this:sphere = sphere(prim[0])

You could have an ID field in your primitive base type, to make it easier to know what the objects actually are.


Czar Flavius(Posted 2009) [#4]
Don't you need this too?
Global Prim:Primtive[] = New Primitive[5]


You might find it simpler to have an abstract setup method, with x, y, z and a few dummy parameters (that default to Null) to cover shape-specific parameters such as radius, or plane size. It's not elegant, but it's straightforward :)

You should change this
	Field mat:material
	Field dir:vector
	
	Method New()
		mat:material=New material
		dir:vector=New vector
	EndMethod
to this
	Field mat:material
	Field dir:vector
	
	Method New()
		mat=New material
		dir=New vector
	EndMethod
or even better, just this
	Field mat:material = New material
	Field dir:vector = New vector



Jesse(Posted 2009) [#5]
Global Prim:Primtive[] = New Primitive[5]


that doesn't work as you would think. It is better the way col has it.


TaskMaster(Posted 2009) [#6]
That line:

Global Prim:Primtive[5]

Has a spelling mistake. Use SuperStrict to solve your problems.


Scienthsine(Posted 2009) [#7]
What they said. You'll probably need to add a field to your base type that is just for reflection. That is, it's a 0 if primitive, 1 if circle, 2 if square, etc. With some constants this can be easy to read. In each of the extended new/create methods you would set that field to the constant that is it's type. Then when you are looking through your primitives, you can check this to see what you should cast to.

Blitzmax has some builtin reflection that may be worth giving a look. With it you could make your list a list of objects and then use it to figure out what each object actually is. You may be able to do it with your code as it is, except I'm not sure if the reflection will return your primitive type, or the extended. Haven't actually used them myself.

[edit]
Ohh and in response to 'Czar Flavius's post. He shouldn't create the five primitives like that. He would have extras he's not using, and would be writing over them, and letting them get collected anyway. It's better the way he has it, with null pointers until he assigns a newly created object to a spot in the array.

And it doesn't matter if he writes mat:material=New material in the new method. Everything you suggested here would be optional. I like the news to be there, and he can specify the type of a variable as many times as he likes throughout code. For instance, I could make a string 'Global txt:String' and later when using it I can still specify the type, like 'Print txt:String'. This is all a matter of style.


col(Posted 2009) [#8]
Thanks all for the help and tips.

Just for the record :) i always use SuperStrict, thats just a typo in my writing in the forum. Sorry.

I forgot about typecasting lol. thanks for the great reminder. I was just trying to make easier to read code. The technique i originally used works ok in c++, and as BMax isnt too far away from c++ i thought i could use something similar, but, at the end of the day its not c++ :)
Maybe it could be implemented in future releases ??

Thanks again and also thanks for the quick replies.


col(Posted 2009) [#9]
Hmm, Just been playing with the debugger.....

I placed a DEBUGSTOP just before prim[0]=new Sphere.
Looking in the DEBUG tab, i see Global prim:primitive[] which is the array, i open it up to see the pointers for the array [0] to [4] are Null as expected.
Then i press the STEP button so the IDE runs the code prim[0]=new sphere.
Looking in the DEBUG tab i see that indeed [0] is filled with data. And...... Its shows the Type Primitive fields and also Type Sphere Fields including address values that are being generated for the other types that are used inside the Sphere Type - ie centre:Vector.

Surely i should be able to access these as i am expecting without the typecasting ???? or is it just the way the debugger is presenting the data? After all Sphere is just Primitive with the extra data and code bolted on.

i just didnt want to use typecasting method ( which does work ), but if i have to then i have to.


Scienthsine(Posted 2009) [#10]
Well, if the casting works... and not doesn't... what do you think needs to be done?

Depending on what your doing, there may be better ways though. If your looping through all primitives, then doing what needs to be done depending on the actual extended primitive, then use a case statement. You already had some reflection methods in the above code. I altered the code enough to run on my machine, look it over. I also use an eachin at the bottom. If you just want the circles out of the primitives, then that sort of thing would be usefull.

Look through this code.




EDIT

Also, I believe if you assign a primitive to a variable made for a sphere, it will only work if it is infact a sphere. It will end up null if not. This is a kinda crude type-checking. If your using the sphere or other extended type alot, then it's more efficient to have a local variable of the extended type, and assign the primitive type to it. If it works (is the correct type), then use the local from there on with no need to cast. The changes are reflected in the actual object since the local is really a pointer, and the pointer will fall out of scope later and be collected.

I can add this to the example if needed.


col(Posted 2009) [#11]
I guess its not too difficult to change the code :)
Thats a great example.
Thankyou.


Czar Flavius(Posted 2009) [#12]
And it doesn't matter if he writes mat:material=New material in the new method. Everything you suggested here would be optional. I like the news to be there, and he can specify the type of a variable as many times as he likes throughout code. For instance, I could make a string 'Global txt:String' and later when using it I can still specify the type, like 'Print txt:String'. This is all a matter of style.
My point was, his style is inconsistant. And it'd be burdonsome to put :material on the end every time ;)


Scienthsine(Posted 2009) [#13]
Aye, I understand. I just don't like to muddy the water things that aren't directly related to the problem at hand.