In FreeBASIC, Type data structures must ultimately be fixed-size, such that the compiler knows how much memory to allocate for objects of that Type.
Nevertheless, a Type may contain dynamic (variable-length) field members: strings and even arrays.
However, the string's/array's data will not be embedded in the Type directly. Instead, the Type will only contain a string/array descriptor structure, which FreeBASIC uses behind the scenes to manage the dynamic string/array data in the heap.
Therefore for sizing the structure of the array descriptor in the Type, a dynamic array member must be always declared by using 'Any' in place of the array bounds, in order to fix the amount of dimensions (this fixing the descriptor size).
1)
When the compiler build a default copy-constructor and a default copy-assignment operator for such a Type (having dynamic array members), it includes all code for sizing the destination array and copying the data from the source array, if needed.
Example 1:
- Automatic array sizing and copying by the compiler:
Code: Select all
Type UDT Dim As Integer array(Any) End Type Dim As UDT u1, u2 Redim u1.array(1 To 9) For I As Integer = Lbound(u1.array) To Ubound(u1.array) u1.array(I) = I Next I u2 = u1 For I As Integer = Lbound(u2.array) To Ubound(u2.array) Print u2.array(I); Next I Print Dim As UDT u3 = u1 For I As Integer = Lbound(u3.array) To Ubound(u3.array) Print u3.array(I); Next I Print Sleep
Code: Select all
1 2 3 4 5 6 7 8 9 1 2 3 4 5 6 7 8 9
If the user want to specify his own copy-constructor and copy-assignment operator (to initialize additional complex field members for example), the above automatic array sizing and copying by compiler is broken.
Example 2:
- Automatic array sizing and copying by the compiler broken by an explicit copy-constructor and copy-assignment operator:
Code: Select all
Type UDT Dim As Integer array(Any) 'user fields Declare Constructor () Declare Constructor (Byref u As UDT) Declare Operator Let (Byref u As UDT) End Type Constructor UDT () 'code for user fields in constructor End Constructor Constructor UDT (Byref u As UDT) 'code for user fields in copy-constructor End Constructor Operator UDT.Let (Byref u As UDT) 'code for user fields in copy-assignement operator End Operator Dim As UDT u1, u2 Redim u1.array(1 To 9) For I As Integer = Lbound(u1.array) To Ubound(u1.array) u1.array(I) = I Next I u2 = u1 For I As Integer = Lbound(u2.array) To Ubound(u2.array) Print u2.array(I); Next I Print Dim As UDT u3 = u1 For I As Integer = Lbound(u3.array) To Ubound(u3.array) Print u3.array(I); Next I Print Sleep
Code: Select all
The elementary dynamic array member cannot be processed as a complex object like a dynamic string member, because there is not implicit assignment (referring to the above example, 'This.array = u.array' is disallowed).
The user must code explicitly the sizing and the copying of the array member.
Example 3:
- Array sizing and copying explicitly set in the user copy-constructor and copy-assignment operator:
Code: Select all
#include "crt/string.bi" Type UDT Dim As Integer array(Any) 'user fields Declare Constructor () Declare Constructor (Byref u As UDT) Declare Operator Let (Byref u As UDT) End Type Constructor UDT () 'code for user fields in constructor End Constructor Constructor UDT (Byref u As UDT) 'code for user fields in copy-constructor if Ubound(u.array) >= Lbound(u.array) Then '' explicit array sizing and copying Redim This.array(Lbound(u.array) To Ubound(u.array)) memcpy(@This.array(Lbound(This.array)), @u.array(Lbound(u.array)), (Ubound(u.array) - Lbound(u.array) + 1) * Sizeof(@u.array(Lbound(u.array)))) End If End Constructor Operator UDT.Let (Byref u As UDT) 'code for user fields in copy-assignement operator if @This <> @u And Ubound(u.array) >= Lbound(u.array) Then '' explicit array sizing and copying Redim This.array(Lbound(u.array) To Ubound(u.array)) memcpy(@This.array(Lbound(This.array)), @u.array(Lbound(u.array)), (Ubound(u.array) - Lbound(u.array) + 1) * Sizeof(@u.array(Lbound(u.array)))) End If End Operator Dim As UDT u1, u2 Redim u1.array(1 To 9) For I As Integer = Lbound(u1.array) To Ubound(u1.array) u1.array(I) = I Next I u2 = u1 For I As Integer = Lbound(u2.array) To Ubound(u2.array) Print u2.array(I); Next I Print Dim As UDT u3 = u1 For I As Integer = Lbound(u3.array) To Ubound(u3.array) Print u3.array(I); Next I Print Sleep
Code: Select all
1 2 3 4 5 6 7 8 9 1 2 3 4 5 6 7 8 9
Another elegant possibility is to keep this sizing/copying, automatically coded by the compiler, but by simply calling it explicitly. For this, an obvious solution for the member array is to no longer put it at the level of the Type itself, but rather in another specific Type, but inherited (seen from the outside, it is exactly the same).
Example 4:
- Simpler example using a base Type including the dynamic array member:
Code: Select all
Type UDT0 Dim As Integer array(Any) End Type Type UDT Extends UDT0 'user fields Declare Constructor () Declare Constructor (Byref u As UDT) Declare Operator Let (Byref u As UDT) End Type Constructor UDT () 'code for user fields in constructor End Constructor Constructor UDT (Byref u As UDT) 'code for user fields in copy-constructor Base(u) '' inherited array sizing and copying from Base copy-constructor End Constructor Operator UDT.Let (Byref u As UDT) 'code for user fields in copy-assignement operator Cast(UDT0, This) = u '' inherited array sizing and copying from Base copy-assignement operator End Operator Dim As UDT u1, u2 Redim u1.array(1 To 9) For I As Integer = Lbound(u1.array) To Ubound(u1.array) u1.array(I) = I Next I u2 = u1 For I As Integer = Lbound(u2.array) To Ubound(u2.array) Print u2.array(I); Next I Print Dim As UDT u3 = u1 For I As Integer = Lbound(u3.array) To Ubound(u3.array) Print u3.array(I); Next I Print Sleep
Code: Select all
1 2 3 4 5 6 7 8 9 1 2 3 4 5 6 7 8 9
- How and Why to Define Constructors, Assignment-Operators, and Destructor for UDTs in FB
- How to Use the Lbound / Ubound Size information of Array in FB