Accessing a member of a Type once I've found it?

BlitzMax Forums/BlitzMax Programming/Accessing a member of a Type once I've found it?

Ian Martin(Posted 2014) [#1]
Hi!
This may be a stupid question, but I am just not seeing an answer to it in the BlitzMAX forums. I've been searching for hours. I see that types are different than they were in Blitz3D, where I would use an Object or Handle? Anyway, this is what I'm trying to do:


For Platform:TypPlatform=EachIn PlatformList
	If ImagesCollide2 (TextureMousePointerTip, MX, MY, 0, 0, 1.0, 1.0, PlatformsFramesArray[Platform.IDNumber], Platform.XCurrentPosition, Platform.YCurrentPosition, 0, 0, 1.0, 1.0) Then
		Platform.XCurrentPosition=MX
		Platform.YCurrentPosition=MY

'What do I do here?
'->		'I need to set a variable or reference to this type instance here so I can access it later

		Exit	'leave the For loop, as a match was found and stored
	EndIf
Next


'later on,
Platform (the platform Type instance from before).Access it like I did when I was in the EachIn For loop
'do stuff to the platform



I hope that made sense. What I am trying to do is go through a list of type members, find the one the mouse tip is touching, store some kind of a reference to it so I can access that particular one later. Basically the mouse clicks on the platform and selects it. This code makes the mouse move the platform around. But what I want to do is be able to go back to that platform later, set it as the active type member, and access all of its type data. That way I could move it around elsewhere in the code. I can't figure out how I do that. I saw some references to type casting it, but I didn't understand it. I need a working code sample that does this sort of thing so I can see what syntax I would use? Or maybe it's simpler than that? Thanks for reading this far :) Can anybody help me with this?


Chapman7(Posted 2014) [#2]
Okay so you could make a Field for the type and call it Activated and set it to true once the images have collided. And then later iterate through the PlatformList to search for Platform.Activated = True.

I know there is an easier way though, It is just currently eluding me.


Derron(Posted 2014) [#3]
method a)



method b)




So in short: you need to store the result in another variable (clickedPlatform). Within a MapEditor you might even have a "hoveredPlatform" to highlight such a object during "object.Draw()".


So you end up having such a thing:




EDIT: just saw your edited post:
If you store an "active" property in a type you sooner or later end up forgetting another one to "deactivate" before. Keeping such "only one per time"-variables in a managerobject keeps your logic way cleaner. But yes, your code gets longer (instead of "if self.active" you have to use "if self = TPlatformManager.clickedObject" - or use a new Getter Method (self.isActive() - which is a TPlatform-method containing "return (TPlatformManager.clickedObject = self)").

PS: if you want to have an "activePlatform" - feel free to add it to the manager. You could even add a "activeList" in the case of selections (multiple objects) etc.

bye
Ron


Chapman7(Posted 2014) [#4]
Credit: Ziggy from a post several years back

Type myType

    Field value:Int

    Method run()
        Print value
	myFunctionA(value) 'value could have been of any other type except self
	Print value
	myFunctionB(Self)
	Print value
    End Method
End Type

Function myFunctionA(o:Int Var) 'this function work's
    o = 10
End Function

Function myFunctionB(o:Object) 'this one not no matter I set o:Object or o:myType ...
    myType(o).value = 20           'Casting won't help too
End Function

Local t:myType = New myType
t.run




For some reason I thought it just created another instance, but it indeed works like a pointer. Test code:

SuperStrict
Global List:TList = CreateList()
Global theoneyouwant:Object


Type Test
	Field str:String
EndType



For Local enum:Int = 0 To 3
	Local A:Test = New Test
	
	If enum = 2 Then
		A.str = "testing"
	Else
		A.str = "lewl"
	EndIf
	
	ListAddLast List, A
Next


For Local e:Test = EachIn List
	If e.str = "testing" Then
		theoneyouwant = e
	EndIf
Next

Print Test(theoneyouwant).str

Test(theoneyouwant).str = "lewl"


For Local e:Test = EachIn List
	Print e.str
Next



Chalky(Posted 2014) [#5]
As soon as you have found theoneyouwant you might want to Exit the loop:
For Local e:Test = EachIn List
	If e.str = "testing" Then
		theoneyouwant = e
		Exit
	EndIf
Next
You'll not notice any benefit with a few objects - but with thousands it could make quite a difference.


Ian Martin(Posted 2014) [#6]
Thanks everybody for the quick answers! I think what Chapman7 (and Ziggy :) came up with is what I was looking for.

This is the part I would have never figured out:
Global theoneyouwant:Object
'and
Test(theoneyouwant).str =


That syntax right there. I couldn't find that anywhere!

I ran the example you gave, altered it a bit, and here's what I came up with:


I forgot to mention I wanted the first result. So I added some extra "testing"s in the code.

Here's the output:
Executing:ObjectPointer.debug.exe...
lewl
lewl
testing
lewl
lewl
testing
lewl
lewl
lewl
testing
lewl

String found: testing

lewl
lewl
I want this one
lewl
lewl
testing
lewl
lewl
lewl
testing
lewl

Process complete

Thanks a bunch for this everybody! This has been a nagging question for me for quite a while. It's a big help :D


Chapman7(Posted 2014) [#7]
It turns out, you dont need to do all that. I thought all this would do, was copy the values, but it turns out it copies the addresses as well

SuperStrict
Global List:TList = CreateList()
Global theoneyouwant:Test


Type Test
	Field str:String
EndType


Print "The List:"
For Local enum:Int = 0 To 3
	Local A:Test = New Test
	
	If enum = 2 Then
		A.str = "testing"
	Else
		A.str = "lewl"
	EndIf
	
	ListAddLast List, A
	Print A.str
Next


Print "~n~n~n"

For Local e:Test = EachIn List
	If e.str = "testing" Then
		theoneyouwant = e
		Exit
	EndIf
Next

Print theoneyouwant.str

theoneyouwant.str = "lewl"

Print "~n~n~nNew List:"
For Local e:Test = EachIn List
	Print e.str
Next


So you can just make it the same Type if you wanted. Either way works obviously, but I just thought this was interesting


EDIT: So I am still kind of surprised by this. How exactly would you copy a type's values (if perhaps, there was a lot) without making a ptr type like above?


TomToad(Posted 2014) [#8]
Types are stored as references, which is a fancy way of saying pointer. When you create an instance of a type with new, you are in a sense creating a pointer to a table of its values.
Type TMyType '<- This defines the type, nothing is actually created until you call new
   Field a:int
   Field b:int
   Field c:int
End Type

Local MyType:TMyType = New TMyType 'an instance of TMyType is created.  A table containing a:int, b:int, c:int is created and MyType holds a reference to that table.
Local YourType:TMyType = New TMyType 'another table is created and the reference to that one is held here.
Local OtherType:TMyType = YourType 'nothing is created here.  The reference to YourType is copied to OtherType.  They both point to the same values.

MyType.a = 10 'The a:int contained within the table pointed to by MyType is changed to 10
YourType.a = 5 'YourType a:int is changed to 5, but since YourType points to the same place as OtherType, OtherType is affected as well.

Print "MyType.a = "+MyType.a
Print "YourType.a = "+YourType.a
Print "OtherType.a = "+OtherType.a



Ian Martin(Posted 2014) [#9]
@Chapman7, Oh, I didn't even realize they are different types!
I added this to my code and now it can select a platform and access that platform without searching through all of them, which is what I was trying to accomplish. I made a
Global SelectedTypeObject:Object
which then holds the last platform selected.

@TomToad, I thought maybe they were pointers...leave it to someone who understands your signature to explain that ;)

So if it's just a reference, it shouldn't cause any memory leak problems? I'm only using one, you can only select one thing at a time, so it's only one reference. I've read about using old style types and memory leaks on a lot of other posts. I don't want that, even if it's just the one object :)


FBEpyon(Posted 2014) [#10]
I usally use a Function Pointer:

Pseudo code:


'**** STORING THE STRING INTO POINTER ****

Global MyPointer:String(v:String)

MyPointer = StoreString

Type My
	Global List:TList = CreateList()
	Field str:String
End Type

Function StoreString:String(v:String)
	For m:My = Eachin My.List
		if m.str = v Then Return m.str
	Next
End Function

'.... Run your code ....

string:String = MyPointer("lewl")


'**** STORING THE OBJECT INTO POINTER ****

Global MyPointer:My(v:String)

MyPinter = StoreObject

Type My
	Global List:TList = CreateList()
	Frield str:String
End Type

Function StoreObject:My(v:String)
	For m:My = Eachin My.List
		If m.str = v Then Return m
	Next
End Function

'.... Run Your Code ....

Object:My = MyPointer("lewl")




You could just call the function itself.. or inbed the pointer into another function.

Like:

Function Search:My(MyPointer(v:String))

End Function

http://www.blitzmax.com/Community/posts.php?topic=99133#1162283


TomToad(Posted 2014) [#11]
So if it's just a reference, it shouldn't cause any memory leak problems? I'm only using one, you can only select one thing at a time, so it's only one reference. I've read about using old style types and memory leaks on a lot of other posts. I don't want that, even if it's just the one object :)

Typically, there wont be memory leak problems. The issue arises when you have more than one reference to an object, then no longer need the object. If you don't remove all the references pointing to the object, then the GC will never release it. This mostly occurs with circular references (one object has a field pointing to another which has a field pointing back).

This code fragment has no memory leak
Local MyObject:TMyObject = new TMyObject
Local SameObject:TMyObject = MyObject
'There is only one object in memory.  Both MyObject and SameObject point to it.

'We remove one reference.  The object is still in memory because SameObject still points to it.
MyObject = Null

'We now remove the second reference
SameObject = Null

'After removing the reference held by SameObject, there are no more pointing to the object, so the GC then removes the object from memory.  It no longer exists.


This code fragment has memory leak
Local MyType:TMyType = New TMyType
Local OtherType:TMyType = New TMyType
'Two objects are created, and a reference to each are created

'Another point of the code not shown here defines TMyType with a field Sibling pointing to a TMyType.
'We will use that field to point one object to the other
MyType.Sibling = OtherType
OtherType.Sibling = MyType

'At this point of the code, we still only have 2 objects in memory, but each object is referenced twice.

'now we don't need the objects anymore, lets get rid of them in the conventional way.
MyType = Null
OtherType = Null

'Wait a minute, the objects are not being removed from memory.  The reason is because
'there still is a reference to each object within the other object, and there is no longer any way to access
'those objects!  That is a memory leak.
'No need to worry too much.  if you are only talking about a couple of objects, they will be removed when the program ends
'but when you have a list with thousands of objects, getting recreated over and over again,
'then you have problems.

'To avoid this problem, you should remove the reference inside the types before removing the main references.
'
'MyType.Sibling = Null
'OtherType.Sibling = Null
'MyType = Null
'OtherType = Null
'
'Or avoid using circular references when you can.


Edit: There is no need to Null a type variable if it is being reused for another object
Local MyType:TMyType = New TMyType
DoStuff(MyType)
MyType:TMyType = New TMyType

is the same as
Local MyType:TMyType = New TMyType
DoStuff(MyType)
MyType = Null
MyType:TMyType = New TMyType

Also no need if the object will exist til the end of the program.


Ian Martin(Posted 2014) [#12]
Thanks again for these answers!
I like the function pointer stuff too. I don't know why, but anything with 'pointer' in it has always confused me...ever since I did some C/C++ a long time ago, heheh

@Tom, I see the difference in those two code samples. I'm not having them refer to each other, so I think I'll be OK. Glad you spelled it out here...makes it very easy to understand :)


FBEpyon(Posted 2014) [#13]
The other thing I have done is do Function with in the type (class)

** UN-TESTED CODE **


Global oldobject:My = New My.Set("lewl")

Type My

	Field str:String
	Global List:TList = CreateList()

	Method Set:My(value:String)
		self.str = value
		My.List.AddLast(Self)
		Return Self
	End Method

	'No Fields
	Function Get:My(value:String)
		For m:My = Eachin My.List
			If value = m.str
				My.List.Remove(m)
				Return New My.Set(value)
			EndIf
		Next
	End Function

	Function Clone:My(Value:String)
		For m:My = Eachin My.List
			If value = m.str
				Return m
			EndIf
		Next
	End Function

	Function Modify(m:My var, value:String)
		For i:My = Eachin My.List
			If value = i.str
				m.str = value
			Endif
		Next
	End Function

	Function Store(value:String Var, test:String)
		For m:My = Eachin My.List
			If test = m.str
				value = m.str
			Endif
		Next
	End Function

End Type

local test:String = "lewl"

local newobject:My = My.Get(test) ' Creates a new one removes old one from list based on the value.

local tmpobject:My = My.Clone(test) ' Creates a new one and clones it base don the value.

''' ALTERN '''''

My.Modify(oldobject, test) ' Modifies the oldobject istead of creating a new one based on the value.

local test2:String

My.Store(test2, test) ' This stores the value (you can use any variable you want Int, Float, etc), into a local or global to be used later.



This can diffently be tweaked to work better, but I hope it helps with getting a object storing the information and then using it.. You will have to create a way to remove it of course when your done with the temp.

** EDIT **

I guess you could just do My.List.Remove(tempobject)

Also the changes are based on the value being put into the function, so test is the global variable of "lewl", and if the list finds anything with that value it will place it into a new, cloned, or modified type..

** ADDED Function Store as well **


TomToad(Posted 2014) [#14]
There is a thread with a very good tutorial on types http://blitzbasic.com/Community/posts.php?topic=59233
The link in the first post doesn't work anymore, but fortunately Brucey was nice enough to host the .pdf file http://brucey.net/programming/blitz/misc/library/BlitzMax_OOP_Tutorial.pdf


FBEpyon(Posted 2014) [#15]
I love types..

I have Methods with embedded Function Pointers for all kinds of things..


col(Posted 2014) [#16]
Hiya,

Just a note about the circular references and memory leaks...

The multi-thread build option uses a different 'mark and sweep' style garbage collector which doesn't suffer the memory leak problem.


Ian Martin(Posted 2014) [#17]
I haven't done much with classes yet. Still getting my mind around types, haven't tried anything like fucntions inside :)
Thanks for the Brucey tutorial link Tom, I downloaded the pdf. Looks pretty good! :D
I haven't tried multi-threading anything yet...thanks for the info about it though, it's good to know for down the road :)