Written by rdc
There are times when creating a program that you may want to define an aggregate structure such as a personnel record, or an enemy in a game. While you can do this using individual data types, it is hard to manage within  a program. Composite data types allow you to group together related data items into a single structure that can be manipulated as a single entity. FreeBASIC offers two composite data types, the 
Type and 
Union.
FreeBASIC allows you to group several data types into a unified structure called a Type definition which you can use to describe these aggregate data structures.
The basic structure of a type definition is:
Type typename
    Var definition
    Var definition
    ...
End Type
The 
Type-
End Type block defines the scope of the definition. You define the elements of the type structure in the same manner as using the 
Dim keyword, without using 
Dim. The following code snippet shows how to build an employee type.
Type EmployeeType
    fname As String * 10
    lname As String * 10
    empid As Integer
    dept As Integer
End Type    
You can use any of the supported data types as data elements, including pointers and other type definitions. When you create the type definition, such as in the example above, you are just creating a template for the compiler. In order to use the type definition, you need to create a variable of the type, as the following code snippet illustrates.
Dim Employee As EmployeeType
Once you have created a variable of the type, you can access each element within the type using the dot notation 
var_name.field_name.  
Using the above example, to access the fname field you would use: 
Employee.fname = "Susan"
To access multiple fields at a time, you can use the 
With-
End With block. The following code snippet shows how to use the 
With block with the above example.
With Employee
    .fname = "Susan"
    .lname = "Jones"
    .empid = 1001
        .dept = 24
End With    
The compiler will automatically bind the variable 
Employee to the individual data elements within the 
With block. Not only does mean that you don't have as much typing, but the structure is optimized and is a bit faster than using the full dot notation.
One advantage to using types in your program is that you can pass the structure to a subroutine or function and operate on the structure as a whole. The following code fragment shows a partial subroutine definition.
Sub UpdateEmployeeDept(ByRef Emp As EmployeeType)
    .
    .
    .
End Sub
Notice that the parameter is qualified with 
Byref. This is important since you want to update the type within the subroutine. There are two parameter passing modes in FreeBASIC: 
Byref and 
Byval.
 ByRef and ByVal: A Quick Introduction 
Byref and 
Byval tell the compiler how to pass a reference to the subroutine or function. When you use 
Byref, or 
By Reference, you are passing a pointer reference to the parameter, and any changes you make to the parameter inside the sub or func will be reflected in the actual variable that was passed. In other words, the 
Byref parameter points to the actual variable in memory.
Byval, or 
By Value, on the other hand makes a copy of the parameter, and any changes you make inside the sub or func are local and will not be reflected in the actual variable that was passed. The 
Byval parameter points to a copy of the variable not the actual variable itself.
The default for FreeBASIC .17 is to pass parameters using 
Byval. In order to change a passed parameter, you need to specify the 
Byref qualifier. In this example, the subroutine updates the the department id of the employee type, so the parameter is qualified as 
Byref so that the subroutine can update the dept field of the type variable.
On the other hand you may not need to update the type as in the following code fragment.
Sub PrintEmployeeRecord(Emp As EmployeeType)
    .
    .
    .
End Sub
In this sub you are just printing the employee record to the screen or a printer and do not need to change anything in the type variable. Here the default 
Byval is used which passes a copy  of the employee record to the sub rather than a reference to the variable. By using 
Byval in this case, you won't accidentally change something in the type variable that you didn't intend to change.
You should only use 
Byref if you intend to change the parameter data. It is much safer to use 
Byval in cases where you need to have the parameter data, but want to prevent accidental changes to the data. These accidental changes generate hard-to-find bugs in your program.
In addition to the intrinsic data types, type fields can also be based on a type definition. Why would you want to do this? One reason is data abstraction. The more general your data structures, the more you can reuse the code in other parts of your program. The less code you have to write, the less chance of errors finding their way into your program. 
Using the 
Employee example, suppose for a moment that you needed to track more dept information than just the department id. You might need to keep track of the department manager, the location of the department, such as the floor or the building, or the main telephone number of the department. By putting this information into a separate type definition, you could use this information by itself, or as part of another type definition such as the Employee type. By generalizing your data structures, your program will be smaller, and much more robust.
Using a type within a type is the same as using one of the intrinsic data types. The following code snippets illustrates an expanded department type and an updated employee type.
Type DepartmentType
    id As Integer
    managerid As Integer
    floor As Integer
End Type        
Type EmployeeType
    fname As String * 10
       lname As String * 10
        empid As Integer
        dept As DepartmentType
End Type
Dim Employee As EmployeeType
Notice that in the Employee definition the dept field is defined as 
DepartmentType rather than as one of the intrinsic data types. To access the department information within the 
Employee type, you use the compound dot notation to access the dept fields.
Employee.dept.id = 24
Employee.dept.managerid = 1012
Employee.dept.floor = 13
The top level of the type definition is 
Employee, so that reference comes first. Since dept is now a type definition as well, you need to use the dept  identifier to access the individual fields within the 
DepartmentType. 
Employee refers to the 
employee type, 
dept refers to the department type and 
id, 
managerid and 
floor are fields within the 
department type.
You can even carry this further, by including a type within a type within a type. You would simply use the dot notation of the additional type level as needed. While there is no limit on the levels of nested type definitions, it gets to be a bit unwieldy when used with several levels.
You can also use the 
With-
End With block with nested types, by nesting the 
With block, as illustrated in the following code snippet.
With Employee
       .fname = "Susan"
        .lname = "Jones"
        .empid = 1001
        With .dept
            .id = 24
            .managerid = 1012
            .floor = 13
        End With
End With
Notice that the second 
With uses the dot notation, 
.dept, to specify the next level of type definitions. When using nested 
With blocks, be sure that you match all the 
End With statements with their correct 
With statements to avoid a compile error.
Extending the idea of data abstraction further, it would be nice to be able to separate the initialization of the department type from the initialization of the employee type. By separating the two functions, you can easily add additional department information as needed. This is where you can use type assignments.
Just as you can assign one intrinsic data type to another, you can assign one type variable to another type variable, providing they share the same type definition.
The following code snippet abstracts the department initialization function and assigns the result to the department type within the 
Employee type.
'This function will init the dept type and return it to caller
Function InitDept(deptid As Integer) As DepartmentType
    Dim tmpDpt As DepartmentType
    Select Case deptid
        Case 24 'dept 24
        With tmpDpt
                    .id = deptid
                    .managerid = 1012
                    .floor = 13
            End With
        Case 48 'dept 48
             With tmpDpt
                    .id = deptid
                    .managerid = 1024
                    .floor  = 12
                End With
        Case Else 'In case a bad department id was passed
                With tmpDpt
                    .id = 0
                    .managerid  = 0
                    .floor  = 0
                End With
    End Select
    'Return the dept info
    Return tmpDpt
End Function
'Create an instance of the type
Dim Employee As EmployeeType
'Initialize the Employee type
With Employee
    .fname = "Susan"
    .lname = "Jones"
    .empid = 1001
    .dept = InitDept(24) 'get dept info
End With
As you can see in the snippet, the dept field of the 
employee type is initialized with a function call. The 
InitDept function returns a 
DepartmentType and the compiler will assign that type to the dept field of the 
Employee record.
By just adding a simple function to the program, you have made the program easier to maintain. If a new department is created, you can simply update the 
InitDept function with the new department information, recompile and the program is ready to go.
There is yet another data type that can be used in type definitions, the bit field. Bit fields are defined as 
variable_name: bits As DataType. The variable name must be followed with a colon, the number of bits, followed by the data type. Only integer types (all numeric types excluding the two floating-point types 'single' and 'double' and excluding also the 64-bit types) are allowed within a bit field. Bit fields are useful when you need to keep track of boolean type information. A bit can be either 
0 or 
1, which may represent Yes or No, On or Off or even Black and White.
The following code snippet illustrates a bit field definition.
Type BitType
    b1: 1 As Integer
    b2: 4 As Integer
End Type
b1 is defined as a single bit, and 
b2 is defined as four bits. You initialize the bitfields by passing the individual bits to the type fields.
myBitType.b1 = 1
myBitType.b2 = 1101
The data type of the bit field determines how many bits you can declare in a bit field. Since an 
integer is 32 bits long, you could declare up to 32 bits in the field. However, in most cases you would declare a single bit for each field, and use a number of fields to define the bit masking that you wish to use. Using a single bit simplifies the coding you need to do to determine if a bit is set or cleared and allows you to easily identify what a bit means within the type definition.
When you create a variable of a type definition, the type is padded in memory. The padding allows for faster access of the type members since the type fields are aligned on a 4 byte or Word boundary. However, this can cause problems when trying to read a type record from a file that is not padded. You can use the use 
field property to change the padding of a type definition.
The 
field keyword is used right after the type name and can have the values 
1, for 1 byte alignment (no padding), 
2 for 2 byte alignment and 
4 for 4 byte alignment. To define a type with no padding you would use the following syntax.
Type myType Field = 1
     v1 As Integer
    v2 As Byte
End Type
For 2 byte alignment you would use 
field = 2. If no 
field = property is assigned, then the padding will be 
4 bytes. If you are reading a type definition created by FreeBASIC using the default alignment, then you do not need to use the 
field property.
Quick Basic
You can initialize a type definition when you dimension the type just as you can any of the intrinsic variables. The following code snippet illustrates the syntax.
Type aType
        a As Integer
        b As Byte
        c As String * 10
End Type
Dim myType As aType => (12345, 12, "Hello")
In the 
Dim statement, the arrow operator 
=> is used to tell the compiler that you are initializing the type variable. The type element values must be enclosed in parenthesis, and separated by commas. The order of the value list corresponds to the order of the type elements, where 
a will be set to 
12345, 
b to 
12 and 
c to 
"Hello".
|  You cannot initialize a dynamic string within a type definition using this method. The string must be fixed length. | 
 
Initializing a type definition in a 
Dim statement is useful when you need to have a set of initial values for a type, or values that will not change during program execution. Since the values are known at compile time, the compiler can doesn't have to spend cycles loading the values during runtime.
Unions look similar to Types in their definition.
Union aUnion
    b As Byte
    s As Short
    i As Integer
End Union
If this were a 
Type, you could access each field within the definition. For a 
Union however, you can only access one field at any given time; all the fields within a 
Union occupy the same memory segment, and the size of the 
Union is the size of the largest member.
In this case, the 
Union would occupy four bytes, the size of an 
Integer, with the 
b field occupying 1 byte, the 
s field occupying 2 bytes, and the 
i occupying the full 4 bytes. Each field starts at the first byte, so the 
s field would include the 
b field, and the 
i field would include both the 
b and 
s fields.
A good example of using a type definition in a union is the 
Large_Integer definition found in 
winnt.bi. The 
Large_Integer data type is used in a number of Windows functions within the C Runtime Library. The following code snippet shows the 
Large_Integer definition.
Union LARGE_INTEGER
    Type
        LowPart As DWORD
        HighPart As Long
    End Type
    QuadPart As LONGLONG
End Union
The 
Dword data type is defined in 
windef.bi as a FreeBASIC 
Uinteger, and the 
Longlong type is defined as a 
Longint. A Long is just an alias for the integer data type. Remember that a type occupies contiguous memory locations, so the 
HighPart field follows the 
LowPart part field in memory. Since this is a union, the type occupies the same memory segment as the 
QuadPart field.
When you set 
QuardPart to a large integer value, you are also setting the values of the type fields, which you can then extract as the 
LowPartand 
HighPart. You can also do the reverse; that is by setting the 
LowPart and 
HighPart of the type, you are setting the value of the 
QuadPart field.
As you can see, using a type within a union is an easy way to set or retrieve individual values of a component data type without resorting to a lot of conversion code. The layout of the memory segments does the conversion for you, providing that the memory segments make sense within the context of the component type.
In the 
Large_Integer case, the 
LowPart and 
HighPart have been defined to return the appropriate component values. Using values other than 
Dword and 
Long would not return correct values for 
LowPart and 
HighPart. You need to make sure when defining a type within a union, you are segmenting the union memory segment correctly within the type definition.
A union within a type definition is an efficient way to manage data when one field within a type can only one of several values. The most common example of this is the Variant data type found in other programing languages.
|  FreeBASIC does not have a native Variant data type at this time. However, by using the extended Type syntax, you could create a Variant data type for use in your program. | 
 
When using a 
Union within a type it is common practice to create an 
id field within the type that indicates what the union contains at any given moment. The following code snippet illustrates this concept.
'Union field ids
#define vInteger 0
#define vDouble 1
'Define type def with variable data fields
Type vType
    vt_id As Integer
    Union
        d As Double
        i As Integer
    End Union
End Type
The union definition here is called an anonymous union since it isn't defined with a name. The 
vt_id field of the type definition indicates the value of the union. To initialize the type you would use code like the following.
Dim myVarianti As vType
Dim myVariantd As vType
myVarianti.vt_id = vInteger
myVarianti.i = 300
myVariantd.vt_id = vDouble
myVariantd.d = 356.56
myVarianti contains an 
integer value so the 
id is set to 
vInteger. 
myVariantd contains a 
double so the id is set to 
vDouble. If you were to create a subroutine that had a 
vType parameter, you could examine the 
vt_type field to determine whether an 
integer or 
double had been passed to the subroutine.
|  You cannot use dynamic strings within a union. | 
 
Using a combination of 
unions and 
types within a program allows you to design custom data types that have a lot of flexibility, but care must be taken to ensure that you are using the data constructs correctly. Improper use of these data types can lead to hard-to-find bugs. The benefits however, out-weigh the risks and once mastered, are a powerful programming tool.