The solution selected is to keep a copy of the data into the linkedlist and to exclude any String type.
The goal is to be as close as possible to the standard PB linkedlist
Notice the associated ForEach:Next macro

Need some help to validate this.
Code: Select all
;=====================================================
; LinkedListEx
;
; An alternative linked-list.
; This linked-list can be embedded into a structure
;
; Dräc - Sept 2007
;=====================================================
Structure LLEx_Element
*next.LLEx_Element
*prev.LLEx_Element
EndStructure
Structure LinkedListEx
*first.LLEx_Element
*current.LLEx_Element
*last.LLEx_Element
Index.l
Count.l
DataLenght.l
EndStructure
;------------------------------- NewListEx ------------------------------
;
; NewListEx allows to declare a new dynamic linked list. Each element of
; the list is allocated dynamically.
; There are no element limits, so there can be as many as needed.
; A such list can contain any:
; - Variables standard except String type,
; - Structured type without any String field
; - Adress
;
; INPUT:
; The size of the Data (use SizeOf() if necessary)
;------------------------------------------------------------------------
Procedure NewListEx(DataLenght.l)
*list.LinkedListEx = AllocateMemory(SizeOf(LinkedListEx))
*list\Index = -1
*list\Count = 0
*list\DataLenght = DataLenght
ProcedureReturn *list
EndProcedure
;------------------------------ ClearListEx -----------------------------
;
; Clears all the elements in this list and releases their memory.
; After this call the list is still usable, but the list is empty
; (i.e. there are no elements in it).
;------------------------------------------------------------------------
Procedure ClearListEx(*list.LinkedListEx)
With *list
\current = \first
While \current
*this = \current
\current = \current\next
FreeMemory(*this)
Wend
\first = #Null
\current = #Null
\last = #Null
\Index = 0
\Count = 0
EndWith
EndProcedure
;---------------------------- DeleteListEx ------------------------------
;
; Clears all the elements in this list and releases their memory
; After this call the list is deleted and its memory free
;------------------------------------------------------------------------
Procedure DeleteListEx(*list.LinkedListEx)
ClearListEx(*list)
FreeMemory(*list)
EndProcedure
;---------------------------- AddElementEx ------------------------------
;
; Adds a new element after the current element or
; as the first item in the list if there are no elements in it.
; This new element becomes the current element of the list.
;
; Copy data from the memory source adress given by *adress into
; the new element
;
; Return the new element adress or zero if the new element could not be
; created
;
; INPUT:
; The adress of the data to store
;------------------------------------------------------------------------
Procedure AddElementEx(*list.LinkedListEx, *adress)
With *list
*this.LLEx_Element = AllocateMemory(SizeOf(LLEx_Element)+\DataLenght)
If *this
;If there is an element in the list, add after the current element
If \Count
*this\prev = \current
If \current\next
*this\next = \current\next
\current\next\prev = *this
Else; the new element is the last element
\last = *this
EndIf
\current\next = *this
\current = *this
Else; No element in the list
\first = *this
\current = *this
\last = *this
EndIf
CopyMemory(*adress, *this+SizeOf(LLEx_Element), \DataLenght)
\Count + 1
\Index + 1
EndIf
ProcedureReturn *this
EndWith
EndProcedure
;-------------------------- InsertElementEx -----------------------------
;
; Inserts a new empty element before the current element or
; at the start of the list if the list is empty.
; This new element becomes the current element of the list.
;
; Copy data from the memory source adress given by *adress into
; the new element
;
; Return the new element adress or zero if the new element could not be
; created
;
; INPUT:
; The adress of the data to store
;------------------------------------------------------------------------
Procedure InsertElementEx(*list.LinkedListEx, *adress)
With *list
*this.LLEx_Element = AllocateMemory(SizeOf(LLEx_Element)+\DataLenght)
If *this
;If there is an element in the list, add before the current element
If \Count
*this\next = \current
If \current\prev
*this\prev = \current\prev
\current\prev\next = *this
Else; the new element is the firt element
\first = *this
EndIf
\current\prev = *this
\current = *this
Else; No element in the list
\first = *this
\current = *this
\last = *this
\Index + 1
EndIf
CopyMemory(*adress, *this+SizeOf(LLEx_Element), \DataLenght)
\Count + 1
EndIf
ProcedureReturn *this
EndWith
EndProcedure
;-------------------------- DeleteElementEx -----------------------------
;
; Remove the current element from the list. After this call, the new
; current element is the previous element (the one before the deleted
; element). If that element does not exist (in other words, you deleted
; the first element in the list) then the new current element is the
; next element
;
; Return the current element adress or zero if the list is empty
;------------------------------------------------------------------------
Procedure DeleteElementEx(*list.LinkedListEx)
With *list
;If there is an element in the list, delete the current element
If \Count
*this.LLEx_Element = \current
;If the current element has a next one, update next link
If *this\next
*this\next\prev = *this\prev
\current = *this\next
\Index + 1
Else; update the list last element link
\last = *this\prev
EndIf
;If the current element has a previous one, update previous link
If *this\prev
*this\prev\next = *this\next
\current = *this\prev
Else; update the list first element link
\first = *this\next
EndIf
\Index - 1
\Count - 1
FreeMemory(*this)
EndIf
ProcedureReturn *this
EndWith
EndProcedure
;---------------------------- NextElementEx -----------------------------
;
; Moves from the current element to the next element in the list
; The next element can't go below the last element.
;
; Return the current element adress.
;------------------------------------------------------------------------
Procedure NextElementEx(*list.LinkedListEx)
With *list
If \current\next
\current = \current\next
\Index + 1
EndIf
ProcedureReturn \current
EndWith
EndProcedure
;-------------------------- PreviousElementEx ---------------------------
;
; Moves from the current element to the previous element in the list
; The previous element can't go above the first element.
;
; Return the current element adress.
;------------------------------------------------------------------------
Procedure PreviousElementEx(*list.LinkedListEx)
With *list
If \current\prev
\current = \current\prev
\Index - 1
EndIf
ProcedureReturn \current
EndWith
EndProcedure
;---------------------------- FirstElementEx ----------------------------
;
; Changes the current list element to the first list element.
;
; Return the current element adress.
;------------------------------------------------------------------------
Procedure FirstElementEx(*list.LinkedListEx)
With *list
\current = \first
\Index = 0
ProcedureReturn \first
EndWith
EndProcedure
;---------------------------- CurrentElement ----------------------------
;
; Return the current element adress.
;------------------------------------------------------------------------
Macro CurrentElementEx(list)
list\current
EndMacro
;----------------------------- LastElementEx ----------------------------
;
; Changes the current list element to the last list element.
;
; Return the current element adress.
;------------------------------------------------------------------------
Procedure LastElementEx(*list.LinkedListEx)
With *list
\current = \last
\Index = \Count-1
ProcedureReturn \last
EndWith
EndProcedure
;------------------------------ ListIndexEx -----------------------------
;
; Return the position of the current element in the list, considering
; that the first element is at the position 0. This function is very fast
; and can be used a lot without performance issue (it doesn't iterate the
; list but uses a cached value).
;------------------------------------------------------------------------
Procedure.l ListIndexEx(*list.LinkedListEx)
With *list
ProcedureReturn \Index
EndWith
EndProcedure
;------------------------------ CountListEx -----------------------------
;
; Counts how many elements there are in the linked list. It does not
; change the current element. This command is very fast (it doesn't
; iterates all the list but uses a cached result) and can be safely used
; to determine if a list is empty or not.
;------------------------------------------------------------------------
Procedure.l CountListEx(*list.LinkedListEx)
With *list
ProcedureReturn \Count
EndWith
EndProcedure
;
; Procedure ResetListEx(*list.LinkedListEx)
; With *list
; \current = \first
; \Index = 0
; EndWith
; EndProcedure
;---------------------------- SelectElementEx ---------------------------
;
; Change the current list element to the element at the specified
; position. The first list element is at position 0, the next at position
; 1 and so on. This is very useful if you want to jump directly to a
; specific position in the klist rather than move through each item
; in turn.
;
; INPUT:
; Index of the element in the list
;------------------------------------------------------------------------
Procedure SelectElementEx(*list.LinkedListEx, Index.l)
With *list
If Index >= 0 And Index < \Count
offset.l = Index - \Index
If offset > 0
While \Index < Index
NextElementEx(*list)
Wend
Else
While \Index > Index
PreviousElementEx(*list)
Wend
EndIf
EndIf
ProcedureReturn \current
EndWith
EndProcedure
;----------------------------- SwapElementsEx ---------------------------
;
; Swaps 2 elements of the specified list. The '*FirstElement' and
; '*SecondElement' parameters must be pointers to valid list elements.
; This function is very useful if you want to sort or reorganize quickly
; a list. It should be used with care and only by advanced users.
;
; INPUT:
; Adresses of the two Elements to swap
;------------------------------------------------------------------------
Procedure SwapElementsEx(*list.LinkedListEx, *FirstElement.LLEx_Element, *SecondElement.LLEx_Element)
With *list
;update the current pointer of the list (Index not change)
If *FirstElement = \current
\current = *SecondElement
ElseIf *SecondElement = \current
\current = *FirstElement
EndIf
;update first and last pointers of the list
If *FirstElement = \first
\first = *SecondElement
If *SecondElement = \last
\last = *FirstElement
;update neigbors
; *FirstElement\prev\next = *SecondElement
*FirstElement\next\prev = *SecondElement
*SecondElement\prev\next = *FirstElement
; *SecondElement\next\prev = *FirstElement
Else
;update neigbors
; *FirstElement\prev\next = *SecondElement
*FirstElement\next\prev = *SecondElement
*SecondElement\prev\next = *FirstElement
*SecondElement\next\prev = *FirstElement
EndIf
ElseIf *FirstElement = \last
\last = *SecondElement
If *SecondElement = \first
\first = *FirstElement
;update neigbors
*FirstElement\prev\next = *SecondElement
; *FirstElement\next\prev = *SecondElement
; *SecondElement\prev\next = *FirstElement
*SecondElement\next\prev = *FirstElement
Else
;update neigbors
*FirstElement\prev\next = *SecondElement
; *FirstElement\next\prev = *SecondElement
*SecondElement\prev\next = *FirstElement
*SecondElement\next\prev = *FirstElement
EndIf
ElseIf *SecondElement = \first
\first = *FirstElement
*FirstElement\prev\next = *SecondElement
*FirstElement\next\prev = *SecondElement
; *SecondElement\prev\next = *FirstElement
*SecondElement\next\prev = *FirstElement
ElseIf *SecondElement = \last
\last = *FirstElement
*FirstElement\prev\next = *SecondElement
*FirstElement\next\prev = *SecondElement
*SecondElement\prev\next = *FirstElement
; *SecondElement\next\prev = *FirstElement
Else
*FirstElement\prev\next = *SecondElement
*FirstElement\next\prev = *SecondElement
*SecondElement\prev\next = *FirstElement
*SecondElement\next\prev = *FirstElement
EndIf
;swap elements
Element.LLEx_Element\prev = *FirstElement\prev
Element.LLEx_Element\next = *FirstElement\next
*FirstElement\prev = *SecondElement\prev
*FirstElement\next = *SecondElement\next
*SecondElement\prev = Element\prev
*SecondElement\next = Element\next
EndWith
EndProcedure
;------------------------- ChangeCurrentElementEx -----------------------
;
; Changes the current element of the specified list to the given new
; element. The new element must be a pointer to another element which
; exists in this list. This function is very useful if you want to
; "remember" an element, and restore it after performing other processing.
; It should be used with care and only by advanced users.
;
; INPUT:
; Adresses of the Elements to find
;------------------------------------------------------------------------
Procedure ChangeCurrentElementEx(*list.LinkedListEx, *Element.LLEx_Element)
With *list
If \current <> *Element
\current = \first
\Index = 0
While \current <> *Element
NextElementEx(*list)
Wend
EndIf
EndWith
EndProcedure
;---------------------------- GetCurrentData ----------------------------
;
; Return the adress of the data that the current element contains.
;------------------------------------------------------------------------
Macro GetCurrentData(list)
list\current+SizeOf(LLEx_Element)
EndMacro
;--------------------------- List loop Macro ----------------------------
;
; ForEach loops through all elements in the specified linked list starting
; from the first element up to the last.
; If the list is empty, ForEach : Next exits immediately.
;------------------------------------------------------------------------
Macro ForEach(list)
; move the current element to the first element
list\current = list\first
list\Index = 0
; loop through all elements and exit after the last
While list\current
EndMacro
Macro Next(list)
; Next element of the list
list\current = list\current\next
list\Index + 1
Wend
;if no current element (all elements crossed), move it to the last one
If list\current = 0
list\current = list\last
list\Index = list\Count -1
EndIf
EndMacro
Code: Select all
IncludeFile "LinkedListEx.pbi"
Structure MyStructure
field1.q
field2.b
EndStructure
*MyStructures.LinkedListEx = NewListEx(SizeOf(MyStructure)); La liste pour stocker les éléments
MyStruct.MyStructure\field1 = 10
MyStruct.MyStructure\field2 = 3
AddElementEx(*MyStructures, MyStruct)
MyStruct.MyStructure\field1 = 20
MyStruct.MyStructure\field2 = 4
AddElementEx(*MyStructures, @MyStruct)
MyStruct.MyStructure\field1 = 30
MyStruct.MyStructure\field2 = 5
AddElementEx(*MyStructures, @MyStruct)
ForEach(*MyStructures)
*MyStruct.MyStructure = GetCurrentData(*MyStructures)
Debug Str(*MyStruct\field1)+"/"+Str(*MyStruct\field2)
Next(*MyStructures)
Debug "FirstElement"
FirstElementEx(*MyStructures)
*MyStruct.MyStructure = GetCurrentData(*MyStructures)
Debug Str(*MyStruct\field1)+"/"+Str(*MyStruct\field2)
Debug "LastElement"
LastElementEx(*MyStructures)
*MyStruct.MyStructure = GetCurrentData(*MyStructures)
Debug Str(*MyStruct\field1)+"/"+Str(*MyStruct\field2)
Debug "------------------------"
Debug "DeleteElementEx"
FirstElementEx(*MyStructures)
NextElementEx(*MyStructures)
DeleteElementEx(*MyStructures)
*MyStruct.MyStructure = GetCurrentData(*MyStructures)
Debug Str(*MyStruct\field1)+"/"+Str(*MyStruct\field2)
Debug "LastElement"
*MyStruct.MyStructure = *MyStructures\last+SizeOf(LLEx_Element)
Debug Str(*MyStruct\field1)+"/"+Str(*MyStruct\field2)
Debug CountListEx(*MyStructures)
Debug ListIndexEx(*MyStructures)
Debug ""
ForEach(*MyStructures)
*MyStruct.MyStructure = GetCurrentData(*MyStructures)
Debug Str(*MyStruct\field1)+"/"+Str(*MyStruct\field2)
Next(*MyStructures)
Debug "------------------------"
Debug "AddElementEx"
MyStruct.MyStructure\field1 = 100
MyStruct.MyStructure\field2 = 30
FirstElementEx(*MyStructures)
AddElementEx(*MyStructures, @MyStruct)
*MyStruct.MyStructure = GetCurrentData(*MyStructures)
Debug Str(*MyStruct\field1)+"/"+Str(*MyStruct\field2)
Debug "LastElement"
*MyStruct.MyStructure = *MyStructures\last+SizeOf(LLEx_Element)
Debug Str(*MyStruct\field1)+"/"+Str(*MyStruct\field2)
Debug CountListEx(*MyStructures)
Debug ListIndexEx(*MyStructures)
Debug ""
ForEach(*MyStructures)
*MyStruct.MyStructure = GetCurrentData(*MyStructures)
Debug Str(*MyStruct\field1)+"/"+Str(*MyStruct\field2)
Next(*MyStructures)
Debug "------------------------"
Debug "InsertElementEx"
MyStruct.MyStructure\field1 = 66
MyStruct.MyStructure\field2 = 64
InsertElementEx(*MyStructures, @MyStruct)
*MyStruct.MyStructure = GetCurrentData(*MyStructures)
Debug Str(*MyStruct\field1)+"/"+Str(*MyStruct\field2)
Debug "LastElement"
*MyStruct.MyStructure = *MyStructures\last+SizeOf(LLEx_Element)
Debug Str(*MyStruct\field1)+"/"+Str(*MyStruct\field2)
Debug CountListEx(*MyStructures)
Debug ListIndexEx(*MyStructures)
Debug ""
ForEach(*MyStructures)
*MyStruct.MyStructure = GetCurrentData(*MyStructures)
Debug Str(*MyStruct\field1)+"/"+Str(*MyStruct\field2)
Next(*MyStructures)
Debug "------------------------"
Debug "InsertElementEx"
MyStruct.MyStructure\field1 = 200
MyStruct.MyStructure\field2 = 11
FirstElementEx(*MyStructures)
InsertElementEx(*MyStructures, @MyStruct)
*MyStruct.MyStructure = GetCurrentData(*MyStructures)
Debug Str(*MyStruct\field1)+"/"+Str(*MyStruct\field2)
Debug "FistElement"
*MyStruct.MyStructure = *MyStructures\first+SizeOf(LLEx_Element)
Debug Str(*MyStruct\field1)+"/"+Str(*MyStruct\field2)
Debug "Count/Index"
Debug CountListEx(*MyStructures)
Debug ListIndexEx(*MyStructures)
Debug ""
ForEach(*MyStructures)
*MyStruct.MyStructure = GetCurrentData(*MyStructures)
Debug Str(*MyStruct\field1)+"/"+Str(*MyStruct\field2)
Next(*MyStructures)
Debug "------------------------"
Debug "SelectElementEx"
FirstElementEx(*MyStructures)
NextElementEx(*MyStructures)
NextElementEx(*MyStructures)
SelectElementEx(*MyStructures, 0)
*MyStruct.MyStructure = GetCurrentData(*MyStructures)
Debug Str(*MyStruct\field1)+"/"+Str(*MyStruct\field2)
Debug "------------------------"
Debug "SelectElementEx"
FirstElementEx(*MyStructures)
NextElementEx(*MyStructures)
NextElementEx(*MyStructures)
SelectElementEx(*MyStructures, 4)
*MyStruct.MyStructure = GetCurrentData(*MyStructures)
Debug Str(*MyStruct\field1)+"/"+Str(*MyStruct\field2)
Debug "------------------------"
Debug "SwapElements"
*El1 = FirstElementEx(*MyStructures)
*El2 = LastElementEx(*MyStructures)
SwapElementsEx(*MyStructures, *El1, *El2)
ForEach(*MyStructures)
*MyStruct.MyStructure = GetCurrentData(*MyStructures)
Debug Str(*MyStruct\field1)+"/"+Str(*MyStruct\field2)
Next(*MyStructures)
Debug "------------------------"
Debug "SwapElements"
*El1 = SelectElementEx(*MyStructures, 3)
*El2 = SelectElementEx(*MyStructures, 1)
SwapElementsEx(*MyStructures, *El1, *El2)
; Test links in on way
ForEach(*MyStructures)
*MyStruct.MyStructure = GetCurrentData(*MyStructures)
Debug Str(*MyStruct\field1)+"/"+Str(*MyStruct\field2)
Next(*MyStructures)
Debug ""
;Test links in the others way
LastElementEx(*MyStructures)
While *MyStructures\current
*MyStruct.MyStructure = GetCurrentData(*MyStructures)
Debug Str(*MyStruct\field1)+"/"+Str(*MyStruct\field2)
*MyStructures\current = *MyStructures\current\prev
*MyStructures\Index - 1
Wend
*MyStructures\current = *MyStructures\first
*MyStructures\Index = 0
Debug "------------------------"
Debug "ClearListEx"
ClearListEx(*MyStructures)
Debug "Count/Index"
Debug CountListEx(*MyStructures)
Debug ListIndexEx(*MyStructures)
ForEach(*MyStructures)
*MyStruct.MyStructure = GetCurrentData(*MyStructures)
Debug Str(*MyStruct\field1)+"/"+Str(*MyStruct\field2)
Next(*MyStructures)
Debug "------------------------"
Debug "AddElementEx"
MyStruct.MyStructure\field1 = 10;"Tom"
MyStruct.MyStructure\field2 = 3
AddElementEx(*MyStructures, @MyStruct)
MyStruct.MyStructure\field1 = 20;"Dick"
MyStruct.MyStructure\field2 = 4
AddElementEx(*MyStructures, @MyStruct)
MyStruct.MyStructure\field1 = 30;"Harry"
MyStruct.MyStructure\field2 = 5
AddElementEx(*MyStructures, @MyStruct)
Debug "Count/Index"
Debug CountListEx(*MyStructures)
Debug ListIndexEx(*MyStructures)
Debug ""
ForEach(*MyStructures)
*MyStruct.MyStructure = GetCurrentData(*MyStructures)
Debug Str(*MyStruct\field1)+"/"+Str(*MyStruct\field2)
Next(*MyStructures)
Debug "------------------------"
Debug "ChangeCurrentElementEx"
*El1 = SelectElementEx(*MyStructures, 0)
*MyStruct.MyStructure = GetCurrentData(*MyStructures)
Debug Str(*MyStruct\field1)+"/"+Str(*MyStruct\field2)
LastElementEx(*MyStructures)
ChangeCurrentElementEx(*MyStructures, *El1)
*MyStruct.MyStructure = GetCurrentData(*MyStructures)
Debug Str(*MyStruct\field1)+"/"+Str(*MyStruct\field2)