Page 1 of 1
Check if ChangeCurrentElement() returns a valid element
Posted: Sun Nov 23, 2014 12:01 pm
by Joubarbe
Hi,
A simple question : how do I check if the pointer in ChangeCurrentElement() "points" to a valid element in the list ? I'm in a program in which I sometimes delete the pointed element before calling ChangeCurrentElement(). Therefore the current element becomes the first one.
Thanks.
Re: Check if ChangeCurrentElement() returns a valid element
Posted: Sun Nov 23, 2014 1:37 pm
by Alex777
ListIndex() will return -1 if the list pointer does not point at a valid element. You could use that?
Re: Check if ChangeCurrentElement() returns a valid element
Posted: Sun Nov 23, 2014 2:01 pm
by STARGÅTE
No.
If the pointer is invalid ChangeCurrentElement() or an other list-function generates a IMA.
You have to check it self, if the pointer is valid before you call this function.
Re: Check if ChangeCurrentElement() returns a valid element
Posted: Sun Nov 23, 2014 3:58 pm
by Joubarbe
Well, that's the question

How do you check if the pointer is valid ?
IMA ? When the pointer is invalid, ChangeCurrentElement() is like a FirstElement().
Re: Check if ChangeCurrentElement() returns a valid element
Posted: Sun Nov 23, 2014 4:17 pm
by luis
Joubarbe wrote:Well, that's the question

How do you check if the pointer is valid ?
How do you check any other pointer ?
If you allocate an object and the allocation is successful you get a valid pointer (<> 0).
Until you free the object, the pointer is valid.
If you free the object, you know the pointer you have it's not valid anymore.
Joubarbe wrote: I sometimes delete the pointed element before calling ChangeCurrentElement()
Then you know it, if you forgot about it than it means you need to revise the logic you are using, or you need to track the pointers you are saving and possibly to mark your pointer as invalid (or removing it altogether) when deleting the pointed element.
Re: Check if ChangeCurrentElement() returns a valid element
Posted: Sun Nov 23, 2014 4:42 pm
by STARGÅTE
Joubarbe wrote:Well, that's the question

How do you check if the pointer is valid ?
IMA ? When the pointer is invalid, ChangeCurrentElement() is like a FirstElement().
ChangeCurrentElement() is same like "CurrentElement() = *Pointer", with out checking.
The only way to check you question:
Code: Select all
Foreach Element()
If *Piointer = @Element()
; Pointer is valid
EndIf
Next
Re: Check if ChangeCurrentElement() returns a valid element
Posted: Sun Nov 23, 2014 4:48 pm
by luis
Joubarbe wrote:
IMA ? When the pointer is invalid, ChangeCurrentElement() is like a FirstElement().
No, doesn't look that way (would be a very bad choice anyway). Why do you say that ?
Code: Select all
NewList lst.i()
For k = 1 To 10
AddElement(lst())
lst() = k
If k = 5
*ptr = @lst() ; save ptr to fifth
EndIf
Next
ResetList(lst())
While NextElement(lst())
Debug lst()
Wend
ChangeCurrentElement(lst(), *ptr) ; jump to fifth
Debug lst() ; the fifth
DeleteElement(lst()) ; delete the fifth
Debug lst() ; the previous one is now the current, the fourth
ChangeCurrentElement(lst(), *ptr) ; jump to the old fifth
Debug lst() ; can be still there in memory an print "5" since only logically deleted and not physically, can cause an IMA, can print a different element, etc.
ChangeCurrentElement(lst(), *ptr + 32768) ; likely causing an IMA, probably out of the page size used internally by the list
Debug lst()
What happens with the last two debug lst() depend on the internal implementation of the list, something you can't make assumptions on.
Also when you delete an element what happens right now (again you can't count on it) is its address is reused when adding a new element, so you would have your original pointer pointing to an element formally valid but still not the same you were originally pointing to.
Code: Select all
NewList lst.i()
For k = 1 To 10
AddElement(lst())
lst() = k
Debug Str(k) + "->" + @lst()
If k = 5
*ptr = @lst() ; save ptr to fifth
EndIf
Next
ChangeCurrentElement(lst(), *ptr) ; jump to fifth
DeleteElement(lst()) ; delete the fifth
For k = 11 To 15
AddElement(lst())
lst() = k
Debug Str(k) + "->" + @lst()
Next
ChangeCurrentElement(lst(), *ptr) ; jump to the old fifth
Debug lst()
In short, if you delete the pointed object the pointer become invalid and you have no reason to not knowing it.
If you really want you can mimic the IsImage(), IsFile(), etc. family of functions using a map with the pointers addresses adding and removing pointers as needed to be able to test for their validity by testing their presence in the map.
Re: Check if ChangeCurrentElement() returns a valid element
Posted: Sat Jan 16, 2016 2:09 pm
by CONVERT
Thanks Stargate.
I did not think of this obvious solution ( If *Pointer = @Element() ) for testing after a ChangeCurrentElement(Element(),*Pointer)
But it seems it does not work.
It's better using the classical ErrorHandler() to detect the error when the .exe is running.
Re: Check if ChangeCurrentElement() returns a valid element
Posted: Sun Jan 17, 2016 1:18 am
by Demivec
I would think a better solution than checking a pointer against possibly every list element's address would be to implement a custom process around each DeleteListElement().
The process would check the address of the element being deleted against the pointer. If there is a match it would either prevent the deletion or set the pointer to null.
Maybe something like this:
Code: Select all
;set pointer to #Null if element is deleted
Macro _DeleteElement(ourList, ourPointer, flags = 0)
If @ourList = ourPointer
ourPointer = #Null
EndIf
DeleteElement(ourList, flags)
EndMacro
;wrap ChangeCurrentElement() to ensure pointer address is not #Null
;If *ptr
; ChangeCurrentElement(lst(), *ptr) ; jump to element if it exists, else no change
;EndIf
or this:
Code: Select all
;only delete if not being pointed to
Macro _DeleteElement(ourList, ourPointer, flags = 0)
If @ourList <> ourPointer
DeleteElement(ourList, flags)
EndIf
EndMacro
Both would be used:
Code: Select all
_DeleteElement(theList(), *ptr)
;or
_DeleteElement(theList(), *ptr, 1)
For completeness something that also resets the pointer for other mass-delete operations would also be needed, such as for ClearList() or ResetList().
Re: Check if ChangeCurrentElement() returns a valid element
Posted: Sun Jan 17, 2016 11:30 am
by CONVERT
It seems that DeleteElement () remove the element from the list, but not for direct access with ChangeCurrentElement ().
You still get the value stored in the deleted element after a ChangeCurrentElement () to this deleted element.
But if you try to delete the same element a second time, there is a fatal error. Without ErrorHandler(), the .exe stops without any error message.
May be my code is wrong, but I don't see where...
Code: Select all
EnableExplicit
Global NewList Gl$()
Structure Sadr
*adr
EndStructure
Global NewMap Gm.Sadr()
Procedure addelem(P1$)
AddElement(gl$())
gl$() = P1$
AddMapElement(Gm(),gl$())
gm()\adr = @gl$()
Debug "@gl$(" + P1$ + ")=" + Str(@gl$())
EndProcedure
addelem("1")
addelem("2")
addelem("3")
ChangeCurrentElement(gl$(),gm("2")\adr)
DeleteElement(gl$())
Debug "-------------- list begin"
ResetList(gl$())
While NextElement(gl$())
Debug gl$()
Wend
Debug "-------------- list end"
ChangeCurrentElement(gl$(),gm("2")\adr)
If @gl$() <> gm("2")\adr
Debug "@gl$() <> gm('2')\adr"
Else
Debug "@gl$() = gm('2')\adr"
EndIf
Debug Str(@gl$()) + " compared to " + Str(gm("2")\adr)
If gl$() = "2"
Debug "gl$() = '2' just before the second DeleteElement(gl$())"
DeleteElement(gl$())
Else
Debug "gl$() <> '2'"
EndIf
Re: Check if ChangeCurrentElement() returns a valid element
Posted: Sun Jan 17, 2016 11:50 am
by freak
CONVERT wrote:It seems that DeleteElement () remove the element from the list, but not for direct access with ChangeCurrentElement ().
In your example, you just got lucky because the element was not freed yet due to the internal list memory management. If you start deleting more elements, the memory will really be released and you will get an access violation. You cannot rely on this.
You can
never reliably verify if an unknown pointer is a valid list element, because list elements get re-used after deletion. You can see that in this simple example:
Code: Select all
NewList a()
For i = 1 To 5
AddElement(a())
a() = i
Next i
FirstElement(a())
*Ptr = @a()
Debug @a()
Debug a()
DeleteElement(a())
AddElement(a())
a() = 42
Debug @a()
Debug a()
ChangeCurrentElement(a(), *Ptr)
Debug @a()
Debug a()
In the above example, *Ptr is a valid pointer at the end, but not to the element to which it originally referred.
This is why you have to take care yourself that you do not keep any pointers to elements that you delete. Such pointers become garbage.
Re: Check if ChangeCurrentElement() returns a valid element
Posted: Sun Jan 17, 2016 12:20 pm
by CONVERT
Thanks a lot for your explanation and your example. Very interesting.
I agree with you, of course. It is very important to delete the pointer when the element is deleted.
I begin understanding why there is no return error on a ChangeCurrentElement().