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().