Getting types back from objects

BlitzMax Forums/BlitzMax Programming/Getting types back from objects

ImaginaryHuman(Posted 2007) [#1]
If I put a variety of different custom types into a linked list, so they are all converted into `objects`, how can I then know what type of object they are when it comes to accessing them? How do I convert from an object back to the original type? Do I need to store some kind of data about each link in the list, that describes what type each link is, and then use a big longwinded select-case type of thing to decide what to do with it?


GfK(Posted 2007) [#2]
Typed straight into reply box but I think it will work
myTypeList:tList = new tList

'create a dummy list
For n = 1 to 10
  m:myType = new myType
  m.x = rand(1,100)
  m.y = rand(1,100)
  myTypeList.AddLast m
next

m:myType = myType(myTypeList.First())
debugLog m.x + ", " + m.y

type myType
  Field x:int
  Field y:int
end type


As for finding out what type of object it is... my first thought would be to return an object (with each of your types having a common field such as 'ident' or whatever). You can then parse this field and cast back to the right Type.


grable(Posted 2007) [#3]
Id go for an aggregate type with a Kind field and its Object value.
And an array of function pointers, using the Kind as index lookup.


SpaceAce(Posted 2007) [#4]
What's wrong with this?

If (MyType(obj))
  Some code...
End If


SpaceAce


GfK(Posted 2007) [#5]
OK had a quick play and I think this does what you're asking for. Can't help thinking there must be a more elegant solution, but it works.

It works by attempting to cast to as many different types as you need. Of course, it can only cast back to one - all the others will be Null.

Global myTypeList:TList = New TList
SeedRnd MilliSecs()

'create a dummy list
For n = 1 To 10
  If Rand(0,1) = 0
	  m:myType0 = New myType0
	  m.x = Rand(1,100)
	  m.y = Rand(1,100)
  myTypeList.AddLast m
	Else
	  mm:myType1 = New myType1
	  mm.a = Rnd(0,1)
	  mm.b = Rnd(0,1)
  myTypeList.AddLast mm
	EndIf
Next

For r:Object = EachIn myTypeList
	m:myType0 = mytype0(r)
    mm:myType1 = myType1(r)
	If m
	  DebugLog "Type 0!"
	EndIf
	If mm
	  DebugLog "Type 1!"
	EndIf
Next

Type myType0
  Field x:Int
  Field y:Int
End Type

Type myType1
  Field a:Float
  Field b:Float
End Type


I did try the "ident" field solution I mentioned above, and it kind of worked. If I put a debugstop after returning an object, I can see the object in the debugger and the ident field is in there, but Blitzmax refuses to compile the code if I attempt to do anything - even read the fields - of an Object.


grable(Posted 2007) [#6]
The long chain of IF's are quite wasteful if you have many different types in the list. same with the select-case over the kind.


GfK(Posted 2007) [#7]
The long chain of IF's are quite wasteful if you have many different types in the list.
How is it wasteful? What's being wasted?


grable(Posted 2007) [#8]
How is it wasteful? What's being wasted?

lets say you have 10000 objects in that list, and 100 different types.. and you have to loop over the list and handle each and everyone via a select-case or if chain.

Thats alot of compares ;)


GfK(Posted 2007) [#9]
Yes it is. But whatever way you do it, you're still going to have one million objects to deal with. A bunch of If statements isn't going to have any impact, in the grand scheme of things.

Anyhoo, rather than ripping my solution to pieces, how about you offer a different way of doing it?


grable(Posted 2007) [#10]
I think 100 compares, instead of 1 index lookup per iteration would be slower, not to mention the nightmare code to maintain.


GfK(Posted 2007) [#11]
So explain how to do it??

Pseudocode based on theory and/or assumption is rarely of any use.


grable(Posted 2007) [#12]
I didnt mean to rip on your sollution ;) and i did give an alternative.
Im not saying If chains are allways bad, just bad if you have to do it often.

Heres some code for my alternate sollution (which could be solved with oop as well):
SuperStrict

Type TType
	Field Kind:Int
	Field Value:Object
EndType

Global PrintHandler:Byte Ptr[]  = [ Byte Ptr PrintPoint, Byte Ptr PrintRect ]
Global TypeList:TList = New TList

Function AddType( kind:Int, value:Object)
	Local tt:TType = New TType
	tt.Kind = kind
	tt.Value = value
	TypeList.AddLast( tt)
EndFunction

Function PrintTypeList()
	Local call:Int(o:Object)
	For Local tt:TType = EachIn TypeList
		call = PrintHandler[ tt.Kind]
		call( tt.Value)
	Next
EndFunction


Const TT_POINT:Int = 0
Const TT_RECT:Int = 1

Type TPoint
	Field X:Int,Y:Int
EndType

Type TRect
	Field X:Int,Y:Int
	Field W:Int,H:Int
EndType

Function PrintPoint:Int( p:TPoint)
	Print "POINT: " + p.X + "," + p.Y
EndFunction

Function PrintRect:Int( r:TRect)
	Print "RECT: " + r.X + "," + r.Y + "," + r.W + "," + r.H
EndFunction


AddType( TT_POINT, New TPoint)
AddType( TT_RECT, New TRect)
PrintTypeList()



ImaginaryHuman(Posted 2007) [#13]
Wow, this is quite a few replies for only a couple hours. Thanks for the suggestions one and all!

I did think about having a common method within each type that returns some kind of identifying information, but then I realized you can't get to the method to call it because you have to cast the object to a valid type before it even knows that the object is a custom type, rather than an array or something. So methods within the type are out of the question. Then I thought that would be slow and pointless anyway because you might as well just store the `kind` as a data field somewhere.

Then I did think, okay, well maybe let's have a `kind` identifier somewhere, whether it be stored along with the object in a custom type (as above), or in some array or other linked list, or in a custom linked list structure. But then this does lead to the obvious problem that you're going to have to basically do a search operation using IF's or Select/Case to match up the kind with the type of object so that you can cast it back properly. Regardless of those who would say this is not a lot of overhead, it rubs harshly against my optimized assembly programming background and I just don't think it's very efficient to be in the position of `not knowing` what kind the object is and having to figure it out each time. That's too much overhead for me (just my preference).

As to the function pointer idea, it might have some merit, but why bother having a `kind` field if you could just store the function pointer there. Ie hard-wire-in the function with the object. This would at least skip all the if-then-else malarky. So then the given function would be a special form of a function just for that data type, but.... the function is now outside of the object (since there's no method pointers - but I did have a solution for that, albeit a little indirect).

Another issue that comes to mind is the question of what actual purpose/benefit I would have from finding out the type at all.

What I'm aiming to do is have generic data, e.g. a byte array containing any kind of data, and then have many different custom types that can take that byte array as a field and then work on it, while also having other types that use the same byte array but obviously doing different things with it and `viewing` it as having a different meaning. e.g. one type thinks the array contains an image, another type thinks the array contains audio samples (just a wild example). I figured the ideal way to store the data generically is a linked list of byte arrays. Then I want to have a linked list of custom types that point to the objects from the data list, using it for whatever purpose. The question remains, how to then activate or use the types and their methods, for which I figured I would need to know first of all what type it is. If I have some kind of script system that needs to, for example, use a bunch of `picture` types, which point to byte arrays that contain generic data, how would the script know to deal with only the picture types and not the audio types? So obviously I need somewhere to either store data describing what type it is to translate it back, or use some clever technique that preserves the type when it becomes an object.

I think one of the problems is I'm thinking of things in terms of a) knowing the object type, b) going into the type and calling specific methods - as if the program flow is centered on the object. I think this is a misthought on my part, because program flow is going to be based on a script program or an array/list holding a custom sequence of objects. Maybe then I only need to know the object type when looking for objects to include in the programs activities, rather than that the objects need to magically execute themselves?

I'm starting to think there are some aspects to `object orientation` that are backwards.