After deeper tests (with FBC 32-bit), I can conclude that:
- 'Erase' (from inside a procedure) seems to not attempt to deallocate some memory (maybe because the array data are not on the heap?)
- Otherwise the code seems to be safe (
except for global fix-len arrays, see post below).
- But the only difference (for a user) when 'Erase' is called from inside a procedure (on fix-len array passed as parameter) is that the array elements are not reset (not cleared for numeric elements or not re-constructed for object elements like UDTs).
An information is returned from the procedure in order to skip the normal call of destructor at end of scope, through the array descriptor passed by reference, that 'Erase' resets (as if it were the descriptor of a var-len array).
A small code to check the behavior related to the constructor/destructor calls (three cases: 'Erase' called on an array name, 'Erase not called, and 'Erase' called on an array passed as parameter):
Code: Select all
Type UDT
Dim AS Integer I
Declare Constructor ()
Declare Destructor ()
End Type
Constructor UDT
Print "constructor ", @This
End Constructor
Destructor UDT
Print "destructor ", @This
End Destructor
Sub erasing (array() As UDT)
Erase array
Print "array erased"
End Sub
For K As Integer = 1 To 3
Scope
Dim array(1 To 2) As UDT
array(1).I = 111
Print
If K = 1 Then
Print "call 'Erase'"
Erase array
Print "array erased"
Elseif K = 2 Then
Print "array not erased"
Else
Print "call Sub 'erasing'"
erasing(array())
End If
Print
Print Ubound(array), array(1).I
Print "end of scope"
End Scope
Print
Print "--------------------"
Print
Next K
Sleep
Code: Select all
constructor 1703568
constructor 1703572
call 'Erase'
destructor 1703572
destructor 1703568
constructor 1703568
constructor 1703572
array erased
2 0
end of scope
destructor 1703572
destructor 1703568
--------------------
constructor 1703568
constructor 1703572
array not erased
2 111
end of scope
destructor 1703572
destructor 1703568
--------------------
constructor 1703568
constructor 1703572
call Sub 'erasing'
destructor 1703572
destructor 1703568
array erased
2 111
end of scope
--------------------
Note: If really 'Erase' (from inside a procedure) does not attempt to deallocate some memory (maybe because the array data are not on the heap), we could replace this skipped deallocation (compared to a var-len array) by a re-construction of the object elements or by a clearing of the numeric elements.
In that case, the object elements (of fix-len arrays) should be destructed at scope end (or program end for global arrays) as the normal behavior when 'Erase' is used on an explicit array name (this would also fix the bug of the post below). Therefore, the trick of resetting the passed descriptor of a fix-len array should be no longer used (more logical to not modify the descriptor of a fix-len array).
Annex 1: Code to demonstrate that 'Erase' presently resets the descriptor of a fix-len array (of UDT) passed as parameter, a trick, but illogical:
(a trick in order that the calling code does not destroy a second time the array elements when program goes out of its scope)
Code: Select all
Type arrayDimensionDescriptor
Dim As Uinteger nbOfElements ' number of elements: (highBound-lowBound+1)
Dim As Integer lowBound ' lbound
Dim As Integer highBound ' ubound
End Type
Type arrayDescriptor
Dim As Any Ptr nullIndexesAddress ' pointer to the real or virtual element: @array(0, 0,...)
Dim As Any Ptr minIndexesAddress ' pointer to the first real element: @array(lbound1, lbound2,...)
Dim As Uinteger globalSize ' "global" size in bytes: (ubound1-lbound1+1)*(ubound2-lbound2+1).....*(size of 1 element)
Dim As Uinteger elementSize ' size of one element in bytes
Dim As Uinteger nbOfDimensions ' number of dimensions
Dim As arrayDimensionDescriptor arrayDimensions(1 to 8) ' max number of dimensions = 8
End Type ' (numbered from 1 to 8)
private Function arrayDescriptorGetPtrFunction (Byval p As Any Ptr) As Any Ptr
Return p
End function
#macro arrayDescriptorPtr(array, p)
Scope
Dim As Function (() As Typeof((array))) As Any Ptr f
f = Cast(Function (() As Typeof((array))) As Any Ptr, @arrayDescriptorGetPtrFunction)
p = f(array())
End Scope
#endmacro
'------------------------------------------------------------------------------------
Sub printArrayDescriptor (Byval p As Any Ptr)
Dim As arrayDescriptor Ptr pu = p
Print "[@array descriptor: "; pu; "]"
Print " @array(all_null_indexes) ="; pu->nullIndexesAddress
Print " @array(all_min_indexes) ="; pu->minIndexesAddress
Print " array_total_size_in_bytes ="; pu->globalSize
Print " array_element_size_in_bytes="; pu->elementSize
Print " number_of_array_dimensions ="; pu->nbOfDimensions
For i As Integer = 1 to pu->nbOfDimensions
Print " [dimension number:"; i; "]"
Print " number_of_elements="; pu->arrayDimensions(i).nbOfElements
Print " min_index ="; pu->arrayDimensions(i).lowBound
Print " max_index ="; pu->arrayDimensions(i).highBound
Next i
End Sub
'------------------------------------------------------------------------------------
Type UDT
Dim AS Integer I
Declare Constructor ()
Declare Destructor ()
End Type
Constructor UDT
Print "constructor ", @This
End Constructor
Destructor UDT
Print "destructor ", @This
End Destructor
Sub erasing (array() As UDT)
Dim As Any Ptr p
arrayDescriptorPtr(array, p)
printArrayDescriptor(p)
Print
Print "Erasing the fix-len array:"
Print
Erase array
printArrayDescriptor(p)
Print
End Sub
Scope
Dim As UDT array(0 To 1)
erasing(array())
Print "Going out the array scope:"
Print
End Scope
Sleep
Code: Select all
constructor 1703572
constructor 1703576
[@array descriptor: 1703540]
@array(all_null_indexes) =1703572
@array(all_min_indexes) =1703572
array_total_size_in_bytes =8
array_element_size_in_bytes=4
number_of_array_dimensions =1
[dimension number: 1]
number_of_elements=2
min_index = 0
max_index = 1
Erasing the fix-len array:
destructor 1703576
destructor 1703572
[@array descriptor: 1703540]
@array(all_null_indexes) =0
@array(all_min_indexes) =0
array_total_size_in_bytes =0
array_element_size_in_bytes=4
number_of_array_dimensions =1
[dimension number: 1]
number_of_elements=0
min_index = 0
max_index = 0
Going out the array scope:
Annex 2: Code to demonstrate that if 'Erase' did not reset the descriptor of a fix-len array (of UDT) passed as parameter, but re-constructed the array elements, all would be right when program would go out of its scope (the re-constructed array elements properly destroyed when program goes out of its scope):
Code: Select all
Type arrayDimensionDescriptor
Dim As Uinteger nbOfElements ' number of elements: (highBound-lowBound+1)
Dim As Integer lowBound ' lbound
Dim As Integer highBound ' ubound
End Type
Type arrayDescriptor
Dim As Any Ptr nullIndexesAddress ' pointer to the real or virtual element: @array(0, 0,...)
Dim As Any Ptr minIndexesAddress ' pointer to the first real element: @array(lbound1, lbound2,...)
Dim As Uinteger globalSize ' "global" size in bytes: (ubound1-lbound1+1)*(ubound2-lbound2+1).....*(size of 1 element)
Dim As Uinteger elementSize ' size of one element in bytes
Dim As Uinteger nbOfDimensions ' number of dimensions
Dim As arrayDimensionDescriptor arrayDimensions(1 to 8) ' max number of dimensions = 8
End Type ' (numbered from 1 to 8)
private Function arrayDescriptorGetPtrFunction (Byval p As Any Ptr) As Any Ptr
Return p
End function
#macro arrayDescriptorPtr(array, p)
Scope
Dim As Function (() As Typeof((array))) As Any Ptr f
f = Cast(Function (() As Typeof((array))) As Any Ptr, @arrayDescriptorGetPtrFunction)
p = f(array())
End Scope
#endmacro
'------------------------------------------------------------------------------------
Sub printArrayDescriptor (Byval p As Any Ptr)
Dim As arrayDescriptor Ptr pu = p
Print "[@array descriptor: "; pu; "]"
Print " @array(all_null_indexes) ="; pu->nullIndexesAddress
Print " @array(all_min_indexes) ="; pu->minIndexesAddress
Print " array_total_size_in_bytes ="; pu->globalSize
Print " array_element_size_in_bytes="; pu->elementSize
Print " number_of_array_dimensions ="; pu->nbOfDimensions
For i As Integer = 1 to pu->nbOfDimensions
Print " [dimension number:"; i; "]"
Print " number_of_elements="; pu->arrayDimensions(i).nbOfElements
Print " min_index ="; pu->arrayDimensions(i).lowBound
Print " max_index ="; pu->arrayDimensions(i).highBound
Next i
End Sub
'------------------------------------------------------------------------------------
Type UDT
Dim AS Uinteger I
Declare Constructor ()
Declare Destructor ()
End Type
Constructor UDT
Print "constructor ", @This
End Constructor
Destructor UDT
Print "destructor ", @This
End Destructor
Sub erasing (array() As UDT)
' get pointer to descriptor
Dim As Integer Ptr p
arrayDescriptorPtr(array, p)
printArrayDescriptor(p)
Print array(0).I
Print
' save descriptor
Dim As Integer I(4 + p[4] * 3)
For K As Integer = Lbound(I) To Ubound(I)
I(K) = p[K]
Next K
Print "Erasing the fix-len array:"
Print
' bugged erase array
Erase array
printArrayDescriptor(p)
Print Cptr(Uinteger Ptr, I(0))[0]
Print
Print "Restoring the descriptor and re-constructing the array elements:"
Print
' restore descriptor
For K As Integer = Lbound(I) To Ubound(I)
p[K] = I(K)
Next K
printArrayDescriptor(p)
Print array(0).I
' re-construct array elements
Dim As Any Ptr p0 = New(Cast(Any Ptr, p[1])) Typeof((array))[p[2] \ p[3]]
Print array(0).I
Print
End Sub
Scope
Dim As UDT array(0 To 1)
array(0).I = 111
erasing(array())
Print "Going out the array scope:"
Print
End Scope
Sleep
Code: Select all
constructor 1703572
constructor 1703576
[@array descriptor: 1703540]
@array(all_null_indexes) =1703572
@array(all_min_indexes) =1703572
array_total_size_in_bytes =8
array_element_size_in_bytes=4
number_of_array_dimensions =1
[dimension number: 1]
number_of_elements=2
min_index = 0
max_index = 1
111
Erasing the fix-len array:
destructor 1703576
destructor 1703572
[@array descriptor: 1703540]
@array(all_null_indexes) =0
@array(all_min_indexes) =0
array_total_size_in_bytes =0
array_element_size_in_bytes=4
number_of_array_dimensions =1
[dimension number: 1]
number_of_elements=0
min_index = 0
max_index = 0
111
Restoring the descriptor and re-constructing the array elements:
[@array descriptor: 1703540]
@array(all_null_indexes) =1703572
@array(all_min_indexes) =1703572
array_total_size_in_bytes =8
array_element_size_in_bytes=4
number_of_array_dimensions =1
[dimension number: 1]
number_of_elements=2
min_index = 0
max_index = 1
111
constructor 1703572
constructor 1703576
0
Going out the array scope:
destructor 1703576
destructor 1703572
[edit]
WARNING: 'Erase' used on a fix-len array passed by parameter causes a crash in FBC 64-bit (only seen afterwards).
Use only FBC 32-bit to run the different examples.