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?
| ||
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? |
| ||
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. |
| ||
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 |
| ||
Credit: Ziggy from a post several years backType 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 |
| ||
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 NextYou'll not notice any benefit with a few objects - but with thousands it could make quite a difference. |
| ||
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 |
| ||
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 wellSuperStrict 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? |
| ||
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 |
| ||
@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 :) |
| ||
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 |
| ||
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. |
| ||
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 :) |
| ||
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 ** |
| ||
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 |
| ||
I love types.. I have Methods with embedded Function Pointers for all kinds of things.. |
| ||
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. |
| ||
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 :) |