Help with Type Extends
BlitzMax Forums/BlitzMax Programming/Help with Type Extends
| ||
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 |
| ||
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. |
| ||
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. |
| ||
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 EndMethodto this Field mat:material Field dir:vector Method New() mat=New material dir=New vector EndMethodor even better, just this Field mat:material = New material Field dir:vector = New vector |
| ||
Global Prim:Primtive[] = New Primitive[5] that doesn't work as you would think. It is better the way col has it. |
| ||
That line: Global Prim:Primtive[5] Has a spelling mistake. Use SuperStrict to solve your problems. |
| ||
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. |
| ||
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. |
| ||
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. |
| ||
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. |
| ||
I guess its not too difficult to change the code :) Thats a great example. Thankyou. |
| ||
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 ;) |
| ||
Aye, I understand. I just don't like to muddy the water things that aren't directly related to the problem at hand. |