Check if ChangeCurrentElement() returns a valid element

Just starting out? Need help? Post your questions and find answers here.
Joubarbe
Enthusiast
Enthusiast
Posts: 714
Joined: Wed Sep 18, 2013 11:54 am
Location: France

Check if ChangeCurrentElement() returns a valid element

Post 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.
Alex777
User
User
Posts: 49
Joined: Sun Nov 16, 2008 12:47 am
Location: Cayman Is.
Contact:

Re: Check if ChangeCurrentElement() returns a valid element

Post by Alex777 »

ListIndex() will return -1 if the list pointer does not point at a valid element. You could use that?
User avatar
STARGÅTE
Addict
Addict
Posts: 2260
Joined: Thu Jan 10, 2008 1:30 pm
Location: Germany, Glienicke
Contact:

Re: Check if ChangeCurrentElement() returns a valid element

Post 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.
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Lizard - Script language for symbolic calculations and moreTypeface - Sprite-based font include/module
Joubarbe
Enthusiast
Enthusiast
Posts: 714
Joined: Wed Sep 18, 2013 11:54 am
Location: France

Re: Check if ChangeCurrentElement() returns a valid element

Post 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().
User avatar
luis
Addict
Addict
Posts: 3895
Joined: Wed Aug 31, 2005 11:09 pm
Location: Italy

Re: Check if ChangeCurrentElement() returns a valid element

Post 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.
"Have you tried turning it off and on again ?"
User avatar
STARGÅTE
Addict
Addict
Posts: 2260
Joined: Thu Jan 10, 2008 1:30 pm
Location: Germany, Glienicke
Contact:

Re: Check if ChangeCurrentElement() returns a valid element

Post 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
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Lizard - Script language for symbolic calculations and moreTypeface - Sprite-based font include/module
User avatar
luis
Addict
Addict
Posts: 3895
Joined: Wed Aug 31, 2005 11:09 pm
Location: Italy

Re: Check if ChangeCurrentElement() returns a valid element

Post 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.
"Have you tried turning it off and on again ?"
User avatar
CONVERT
Enthusiast
Enthusiast
Posts: 130
Joined: Fri May 02, 2003 12:19 pm
Location: France

Re: Check if ChangeCurrentElement() returns a valid element

Post 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.
PureBasic 6.20 beta 2 (x64) | Windows 10 Pro x64 | Intel(R) Core(TM) i7-8700 CPU @ 3.20Ghz 16 GB RAM, SSD 500 GB, PC locally assembled.
Come back to 6.11 LTS 64 bits because of an issue with #PB_ComboBox_UpperCase in ComboBoxGadget() (Oct. 10, 2024).
User avatar
Demivec
Addict
Addict
Posts: 4282
Joined: Mon Jul 25, 2005 3:51 pm
Location: Utah, USA

Re: Check if ChangeCurrentElement() returns a valid element

Post 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().
User avatar
CONVERT
Enthusiast
Enthusiast
Posts: 130
Joined: Fri May 02, 2003 12:19 pm
Location: France

Re: Check if ChangeCurrentElement() returns a valid element

Post 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
PureBasic 6.20 beta 2 (x64) | Windows 10 Pro x64 | Intel(R) Core(TM) i7-8700 CPU @ 3.20Ghz 16 GB RAM, SSD 500 GB, PC locally assembled.
Come back to 6.11 LTS 64 bits because of an issue with #PB_ComboBox_UpperCase in ComboBoxGadget() (Oct. 10, 2024).
freak
PureBasic Team
PureBasic Team
Posts: 5948
Joined: Fri Apr 25, 2003 5:21 pm
Location: Germany

Re: Check if ChangeCurrentElement() returns a valid element

Post 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.
quidquid Latine dictum sit altum videtur
User avatar
CONVERT
Enthusiast
Enthusiast
Posts: 130
Joined: Fri May 02, 2003 12:19 pm
Location: France

Re: Check if ChangeCurrentElement() returns a valid element

Post 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().
PureBasic 6.20 beta 2 (x64) | Windows 10 Pro x64 | Intel(R) Core(TM) i7-8700 CPU @ 3.20Ghz 16 GB RAM, SSD 500 GB, PC locally assembled.
Come back to 6.11 LTS 64 bits because of an issue with #PB_ComboBox_UpperCase in ComboBoxGadget() (Oct. 10, 2024).
Post Reply