"Typecasting" a TList

BlitzMax Forums/BlitzMax Programming/"Typecasting" a TList

chimaera(Posted 2015) [#1]
Hi all,

A very quick question purely out of interest.

In MonkeyX you need to type cast a List like this:
Global myList:List<c_myOwnObjects> = New List<c_myOwnObjects>

In my experience you do not have to do this in Bmax:
Global myList:Tlist = New Tlist

Can you extend a Tlist in Bmax to only include a certain custom Type, to for example be able to do the following: myList.First.myObjectValue = 5?


Yasha(Posted 2015) [#2]
Doing so for integers would be harder as ints aren't objects. However, for object types this is pretty simple: just extend TList and override all of the value-handling methods to introduce conversions/barriers between object and the type you want the list to be able to handle:

Type Foo
	Field a:Int, b:Int, c:Int
End Type

Type FooList Extends TList
	'....

	Method AddLast:TLink( value:Foo )
		Return Super.AddLast(value)
	End Method

	Method First:Foo()
		Return Foo(Super.First())
	End Method

	'....
End Type


This is a lot of typing, but it effectively provides you with the list type that would be the same as List<Foo> in a language that supported such constructs. (Obviously the MonkeyX way is infinitely better, but if it doesn't exist in Max, it doesn't exist.)

Most people don't bother doing this in BlitzMax and just cast from Object at the point of reading from the list. It's also simple enough to use a generic Object list while you build it, and then convert to an array of the right element type (with ToArray) when you're done adding items.


You could make an int list as well, but you'd need to rewrite the list's internal mechanics to handle non-object values, which is a lot more work than just wrapping an unmodified list in type checks. The standard BlitzMax convention - as seen in BRL.Reflection and so on - for handling primitives is to convert them to strings. Strings are objects, and can thus be handled by normal containers.


Derron(Posted 2015) [#3]
If you extend TList and create a custom "myTypeFirst()"-function then you could iterate over all entries and return the first "mytype(entry) <> null" hit.

Within for loops you already skip other entries:

For local o:myObject = EachIn list
  'only objects of type "myObject" or extended from "myObject"
  o.myObjectValue = 5
Next


To return the first you do something in the likes of:
Function MyFirst:myObject()
  For local o:myObject = EachIn list
    return o
  Next
  'no child of type myObject in the list
  return null
End Function



Edit: ok meanwhile Yasha posted something, so appendum:
When overriding "addLast" and "addFirst" you might include a
if not myObject(o) then Throw "Trying to Add wrong type to list."

So it does not append "wrong" objects to the list.

bye
Ron


ziggy(Posted 2015) [#4]
@Derron, ForEach iteration on a list is generating garbage (the iterator). Casting First if not null should be faster and cleaner


Derron(Posted 2015) [#5]
But the garbage is collected ...

It is always a matter of "convenience" versus "code efficiency".


What do you mean with "Casting First if not null" ?
myobject( list.ValueAtIndex(i) ) ?
(this is really slow)

I collected some variants i just tested:
SuperStrict
Framework Brl.StandardIO
Import Brl.LinkedList


Type A
End Type

Type B
End Type


global list:TList = CreateList()
Global start:int

For local i:int = 1 to 10000
	if i <= 9999
		list.addLast(new A)
	else
		list.addLast(new B)
	endif
Next


start = Millisecs()
For local o:B = EachIn list
	print "found B"
Next
print "1: took: "+(Millisecs() - start)+" ms."


start = Millisecs()
For local i:int = 0 until list.count()
	if B(list.ValueAtIndex(i)) then print "found B"
Next
print "2: took: "+(Millisecs() - start)+" ms."


start = Millisecs()
global link:TLink = list.FirstLink()
While link
	if B(link.Value()) then print "found B"
	link = link.NextLink()
Wend
print "3: took: "+(Millisecs() - start)+" ms."


Link is of course the fastest one.


bye
Ron