This topic has come up before many times. Here's my version of the age old questions on how to copy a structure with strings and how to delete the same type of structure without causing a memory leak.
Th code should be very explanatory, including a simple demonstration.
A brief bit of explanation. It requires info about each structure that will be operated on. This information includes mainly the offsets to the strings within the structure and the size of string arrays. It then relies on this information when copying or deleting the structure. I will probably modify it a little bit more, though it currently works as advertised. It relies on a method posted by freak (Timo).
Code: Select all
;Program: CopyDeleteStringStruct.pb v1.0
;Author: Demivec (Jared), method for freeing string memory supplied by freak (Timo)
;Date: 08/11/2008
;Using: PureBasic v4.20
;Description: the program requires data to be prepared in a DataSection regarding structure types.
;It provides an Enumeration outline to produce type#'s and 3 procedures.
; - registerStructs() initializes the system with data regarding structure types and the offsets
;within them of strings and string arrays. This procedure must be called first before it can be used.
; - deleteStruct() takes a pointer to a registered structure type and frees the memory and strings allocated to it.
; - copyStruct() takes a pointer to a registered structure type and copies it, making true copies of its
;strings that are placed at unique addresses. If a destination pointer to a pre-existing registered structure type
;is not passed it will allocate a new structure to contain the copy. It returns the address of the structure copy
;upon completion.
;
;If a structure doesn't contain any strings it can still be registered and used with the procedures
;so that it can be treated in a consistent manner as the other structures. This is otherwise not
;necessary because it doesn't have the complication of strings.
;
;These procedures are expressly written to properly handle structures with strings or string arrays as subelements.
;They do not handle stuctures within structures, nor do they handle pointers to structures with a structure, nor
;arrays of structures that contain strings.
;sample of 4 structures to be registered
Structure custom
l.b[4]
b.q
d.s
e.s
f.w
EndStructure
Structure custom2
a.s
b.i
d.s
f.w
g.s
h.s
EndStructure
Structure custom3
b.i
EndStructure
Structure custom4
b.i
e.s[11]
f.i
EndStructure
Enumeration ;structure type#'s, for use with StructData and deleteStruct()
#typ_custom ;include an entry for each type of structure to be freed this way
#typ_custom2
#typ_custom3
#typ_custom4
#numOfStructs ;this has to be the last item in this set of enumerations
EndEnumeration
Structure structInfo
size.i ;size of structure ;currently not used
count.i ;number of strings to free
arrayCount.i ;count of string arrays present
offset.i [11] ;structure offset to each string, needs to be dimmed to the maximum possible (count + arraycount)
arraySize.i[6] ;the Offset and Dimension of each array (i.e. [0] = offset to 1st array,[1] = dimensions of 1st array)
EndStructure
Global Dim structInfo.structInfo(#numOfStructs)
DataSection
StructData:
Data.i #numOfStructs ;number of structures in this section followed by an entry for each structure
;structures enumerated type#, structure size, count of strings, count of string arrays
;structure offset of each string(if any)..., structure offset of each string array (if any)...,
;# of elements in each string array(if any)...
;(i.e. minimum entry = data.i type, size, 0, 0)
Data.i #typ_custom, SizeOf(custom), 2, 0, OffsetOf(custom\d), OffsetOf(custom\e)
Data.i #typ_custom2, SizeOf(custom2), 4, 0, OffsetOf(custom2\a), OffsetOf(custom2\d), OffsetOf(custom2\g), OffsetOf(custom2\h)
Data.i #typ_custom3, SizeOf(custom3), 0, 0
Data.i #typ_custom4, SizeOf(custom4), 0, 1, OffsetOf(custom4\e), 11
EndDataSection
Procedure registerStructs() ;this is called once to initialize structInfo() for the deleteStruct() procedure
Protected type, countStruct, i, j
Restore StructData
Read.i countStruct
For i = 1 To countStruct
Read.i type
Read.i structInfo(type)\size
Read.i structInfo(type)\count
Read.i structInfo(type)\arrayCount
For j = 0 To structInfo(type)\count + structInfo(type)\arrayCount - 1
Read.i structInfo(type)\offset[j]
Next
For j = 0 To structInfo(type)\arrayCount - 1
Read.i structInfo(type)\arraySize[j] ;size of string array, = # of elements in array
Next
Next
EndProcedure
Procedure deleteStruct(type, *p) ;frees memory and strings allocated to a registered structure of the enumerated "type"
;frees strings followed by the structure's allocated memory
Protected count = structInfo(type)\count - 1, arrayCount = structInfo(type)\arrayCount - 1, i, j
If *p <> 0
;handles single strings
If count <> -1
Protected Dim String.String(count)
For i = 0 To count
PokeI(@String(i), PeekI(*p + structInfo(type)\offset[i]))
Next
Dim String.String(0)
Else
count = 0
EndIf
If arrayCount <> -1
For j = 0 To arrayCount
Protected Dim String.String(structInfo(type)\arraySize[j])
For i = 0 To structInfo(type)\arraySize[j] - 1
PokeI(@String(i), PeekI(*p + structInfo(type)\offset[count + j] + i * SizeOf(String)))
Next
Dim String.String(0)
Next
EndIf
FreeMemory(*p)
*p = 0
EndIf
EndProcedure
Procedure copyStruct(type, *s, *d = 0) ;return the address (*d) to a string safe copy of structure (*s) of the enumerated "type"
;the destination structure *d can be preexisting, in which case it's contents will be overwritten
Protected *ss.String, *ds.String, count = structInfo(type)\count - 1, arrayCount = structInfo(type)\arrayCount - 1, i, j
;copy entire structure
If *d = 0
*d = AllocateMemory(structInfo(type)\size)
EndIf
CopyMemory(*s, *d, structInfo(type)\size)
;establish new string pointers in copy
If *s <> 0
;handles single strings
If count <> -1
For i = 0 To count
*ss = *s + structInfo(type)\offset[i]
*ds = *d + structInfo(type)\offset[i]
PokeI(*ds, 0)
If PeekI(*ss) <> 0 ;avoid creating empty string out of a null string
*ds\S = *ss\S
EndIf
Next
Else
count = 0
EndIf
If arrayCount <> -1
For j = 0 To arrayCount
For i = 0 To structInfo(type)\arraySize[j] - 1
*ss = *s + structInfo(type)\offset[count + i] + i * SizeOf(String)
*ds = *d + structInfo(type)\offset[count + i] + i * SizeOf(String)
PokeI(*ds, 0)
If PeekI(*ss) <> 0 ;avoid creating empty string out of a null string
*ds\S = *ss\S
EndIf
Next
Next
EndIf
EndIf
ProcedureReturn *d ;return address of copy
EndProcedure
;-an attempt at a demo
registerStructs() ;needs to be called first
;create some sample structures
Define *g.custom4 = AllocateMemory(SizeOf(custom4))
;assign a string to distinguish our structure
*g\e[5] = "still here"
Define *h.custom4 = AllocateMemory(SizeOf(custom4))
;assign a string to show this is a different structure
*h\e[3] = "different place"
;prepare a pre-existing structure to copy into
Define *g2.custom4 = AllocateMemory(SizeOf(custom4))
Debug "pre-existing address for copies: " + Str(*g2)
Debug "------contents of 2 elements before-----"
Debug *g2\e[3] : Debug *g2\e[5] ;empty
Debug "--------------------------"
Debug "address of copy #1: " + Str(copyStruct(#typ_custom4, *g, *g2))
Debug "------contents of 2 elements after copy#1-----"
Debug *g2\e[3] : Debug *g2\e[5]
Debug "--------------------------"
;make a copy to pre-existing structure overwriting it's data
Debug "address of copy #2: " + Str(copyStruct(#typ_custom4 , *h, *g2))
Debug "------contents of 2 elements after copy#2-----"
Debug *g2\e[3] : Debug *g2\e[5]
;delete 2 of the 3 structures
deleteStruct(#typ_custom4, *g)
deleteStruct(#typ_custom4, *h)
Debug "-==============-"
Define *f.custom = AllocateMemory(SizeOf(custom))
;assign a string to demonstrate
*f\d = "hello"
Debug "the original structure's string element:" + Chr(34) + *f\d + Chr(34)
;copy without preallocating the structure
Debug "copies without preallocating"
Define *f2.custom = copyStruct(#typ_custom, *f)
Debug "address of original: " + Str(*f)
Debug "address of new copy: " + Str(*f2)
Debug "the copy structure's string element:" + Chr(34) + *f2\d + Chr(34)
Debug "---------------------------"
;change original string element
*f\d = "goodbye"
Debug "changed the original structure's string element to:" + Chr(34) + *f\d + Chr(34)
Debug "the copy structure's string element remains unchanged:" + Chr(34) + *f2\d + Chr(34)
;delete the original structures
deleteStruct(#typ_custom, *f)