Page 1 of 1

Get if element is valid from pointer?

Posted: Sat Aug 03, 2019 9:37 am
by Joubarbe
Hi,

It's probably been asked before but anyway... I'd like to verify the validity of a list element from its pointer, without going into a ForEach loop, for performance reasons.

Code: Select all

NewList lst.i()

AddElement(lst())
lst() = 12
AddElement(lst())
lst() = 15
*a = @lst()
DeleteElement(lst())

ChangeCurrentElement(lst(), *a)
DeleteElement(lst())
Will obviously gives a memory address error on the last line.

Code: Select all

If IsValidElement(lst(), *a)
  ChangeCurrentElement(lst(), *a)
  DeleteElement(lst())
EndIf
Would be nice.
I'd like to avoid putting my pointer at 0 after removing it the first time (and do a If *a = 0 check), because on a big source code, it can be a pain to do that everywhere. My Delete() function should ensure that the thing I want it to delete actually exists.

(too bad ChangeCurrentElement() doesn't return anything)

Re: Get if element is valid from pointer?

Posted: Sat Aug 03, 2019 11:06 am
by mk-soft
It's not gonna work that way. With ChangeCurrentElement you always had to work very carefully.
If the order of the elements is not so important, I always work with "maps" and then also not with pointers.

Another way is to add a lock variable to the elements (refCount) and only delete the element when it is set to zero.

Re: Get if element is valid from pointer?

Posted: Sat Aug 03, 2019 11:35 am
by mestnyi
Obviously you need to delete the pointer yourself.

Code: Select all

NewList lst.i () 

AddElement (lst ()) 
lst () = 12 
AddElement (lst ()) 
lst () = 15 

*a = @lst () 
DeleteElement (lst ()) 
*a = 0

If *a
  ChangeCurrentElement (lst (), *a) 
  DeleteElement (LST ())
  *a = 0
EndIf
or check

Code: Select all

NewList lst.i () 

AddElement (lst ()) 
lst () = 12 
AddElement (lst ()) 
lst () = 15 

*a = @lst () 
DeleteElement (lst ()) 

Procedure IsValidElement(List Lst.i(), *a) 
  ForEach Lst()
    If Lst() = *a
      ProcedureReturn 1
    EndIf
  Next
EndProcedure

If IsValidElement(lst (), *a)
  ChangeCurrentElement (lst (), *a) 
  DeleteElement (LST ())
EndIf
What does it look like?
mk-soft wrote:Another way is to add a lock variable to the elements (refCount) and only delete the element when it is set to zero.

Re: Get if element is valid from pointer?

Posted: Sat Aug 03, 2019 11:51 am
by Little John
Joubarbe wrote:I'd like to avoid putting my pointer at 0 after removing it the first time (and do a If *a = 0 check)
Well, at least currently, while PureBasic does not have an IsValidElement() function or something similar, doing so is actually a good idea.
Joubarbe wrote:(too bad ChangeCurrentElement() doesn't return anything)
I agree. A proper return value of ChangeCurrentElement() would probably be sufficient for solving problems like the one you encountered. I made a corresponding feature request.

When playing with your above code, I also discovered a related bug in ChangeCurrentElement().

Re: Get if element is valid from pointer?

Posted: Sat Aug 03, 2019 11:59 am
by Joubarbe
Yes, when writing the code above, I wondered if that was not a bug that ChangeCurrentElement() did not return an error. I'm not entirely sure of the answer; maybe some people want this behavior.

Anyway, thank you all. I'll reset my pointer to 0 I guess :)

Re: Get if element is valid from pointer?

Posted: Sat Aug 03, 2019 12:29 pm
by Little John
Joubarbe wrote:Yes, when writing the code above, I wondered if that was not a bug that ChangeCurrentElement() did not return an error. I'm not entirely sure of the answer; maybe some people want this behavior.
As I already wrote, ChangeCurrentElement() should return 0 on error, and non-zero on success. That won't hurt anyone, and everyone then can decide her/himself what s/he is going to do with that information.

Re: Get if element is valid from pointer?

Posted: Sat Aug 03, 2019 12:38 pm
by Joubarbe
But what is success then? If the pointer points to an address that is not overwritten by the system, there will be a valid output:

Code: Select all

NewList lst.i()

AddElement(lst())
lst() = 12
AddElement(lst())
lst() = 15
*a = @lst()
DeleteElement(lst())

ChangeCurrentElement(lst(), *a)
Debug lst() ;DeleteElement(lst())
Still outputs 15. I'm afraid that checking the validity of the new element would mean to go through all the list. If not, that means that there's some way to know if a given address is "out of range" (and that would answer my original question).

Re: Get if element is valid from pointer?

Posted: Sat Aug 03, 2019 12:58 pm
by freak
Keep in mind that the Memory for List items is reused:

Code: Select all

NewList lst.i()

AddElement(lst())
lst() = 123
*a = @lst()
Debug @lst()

DeleteElement(lst())

AddElement(lst())
lst() = 999
Debug @lst()                     ; oops, same value!


ChangeCurrentElement(lst(), *a)  ; this points to a valid element so the result would be "success" (if there was a return value)
Debug lst()                      ; ... but its not the element you want!
Once you call DeleteElement(), the element pointer becomes invalid. It no longer belongs to you. It can be reused for anything (new memory allocations). Remembering it past the DeleteElement() and trying to use it again is a programming error.

Re: Get if element is valid from pointer?

Posted: Sat Aug 03, 2019 1:01 pm
by mk-soft
Managed over RefCounter...

Update v0.6
- Added DeleteListElement
- Save List Position

Code: Select all

;-TOP

EnableExplicit

Structure udtMyList
  refCount.i
  iVal.i
EndStructure

Global MutexMyList = CreateMutex()

Procedure GetListElement(List MyList.udtMyList())
  LockMutex(MutexMyList)
  MyList()\refCount + 1
  UnlockMutex(MutexMyList)
  ProcedureReturn @MyList()
EndProcedure

Procedure FreeListElement(List Mylist.udtMyList(), *ppMyList.Integer, Delete = #True)
  Protected *pMyList.udtMyList
  LockMutex(MutexMyList)
  If *ppMyList\i
    *pMyList.udtMyList = *ppMyList\i
    If *pMyList\refCount > 0
      *pMyList\refCount - 1
      If *pMyList\refCount = 0 And Delete = #True
        If @Mylist() = *pMyList
          DeleteElement(Mylist())
        Else
          PushListPosition(MyList())
          ChangeCurrentElement(Mylist(), *pMyList)
          DeleteElement(Mylist())
          PopListPosition(Mylist())
        EndIf
      EndIf
    EndIf
    *ppMyList\i = 0
  EndIf
  UnlockMutex(MutexMyList)
EndProcedure

Procedure DeleteListElement(List MyList.udtMyList())
  LockMutex(MutexMyList)
  If MyList()\refCount = 0
    DeleteElement(MyList())
  EndIf
  UnlockMutex(MutexMyList)
EndProcedure

Global NewList MyData.udtMyList()

AddElement(MyData())
MyData()\iVal = 1
AddElement(MyData())
MyData()\iVal = 2
AddElement(MyData())
MyData()\iVal = 3
AddElement(MyData())
MyData()\iVal = 4


SelectElement(MyData(), 2)
Debug "Get Pointer A"
Define *a.udtMyList = GetListElement(MyData())
Debug "Get Pointer B"
Define *b.udtMyList = GetListElement(MyData())
Debug "Get Pointer C"
SelectElement(MyData(), 1)
Define *c.udtMyList = GetListElement(MyData())

ForEach MyData()
  Debug "refCount = " + MyData()\refCount + " | Value = " + MyData()\iVal
Next

Debug "Free Pointer A"
FreeListElement(MyData(), @*a)
ForEach MyData()
  Debug "refCount = " + MyData()\refCount + " | Value = " + MyData()\iVal
Next

Debug "Free Pointer B"
FreeListElement(MyData(), @*b)
ForEach MyData()
  Debug "refCount = " + MyData()\refCount + " | Value = " + MyData()\iVal
Next

Debug "Free all unused ListElements"
ForEach MyData()
  DeleteListElement(MyData())
Next
ForEach MyData()
  Debug "refCount = " + MyData()\refCount + " | Value = " + MyData()\iVal
Next

Debug "Free Pointer C"
FreeListElement(MyData(), @*c)
ForEach MyData()
  Debug "refCount = " + MyData()\refCount + " | Value = " + MyData()\iVal
Next