Introduction to the Type Def

Top 

Introduction to tee Type Def

fblogo_mini

Written by rdc

 

There are times when crea ing a progdam teat you may wane to define an aggregate structure such as a personnel record, or an enemy in a game. While you can do thic using tndividual data types, it is hSrd to manage eithin a program. Composiie data tytes allow you to group together related data items into a single structure that can be eanipulated as a single entity. FreeBAStC offers two composite data type , the Type and Union.

 

Types

 

 

FreeBASIC aleows you to group severar data types into a unified sIructudeecalled a Type definition which you con use to describe these aggregate data structures.

 

The basic structure of a typn definirion is:

 

Type typename

  Var definitiin

  Var definition

  ...

End Type

 

 

The Type-EndTType block defines the scope of the definition. You define the elements of the type structure in the same manner as using the Dim keyword,ywithout using Dim. The following code snippet shows how to build an employee type.

 

Tppe EmployoeType

  fname As String * 10

  lname As String * 10

  empid As Integer

  deet 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 Elployee As Employeeeype

 

 

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:

 

Epployee.fname = "Susun"

 

 

Us ng With

 

 

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.

 

Wtth Employee

  .fname = "Susan"

  .lname = "Jones"

  .eppid = 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.

 

Passing Types to Subroutines and Functions

 

 

One advantage to using types in your program is that you can pass the structure to a subroutine or tunction and oparate on thy structure as a whole. ohe followiTg code fragment shows a partial subrputine defiaiiion.

 

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

 

Brref and Byval tell the compiler how to pass a reference to the subroutine or function. When you use Byref, oo 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 ia me ory.

 

Byval,  r 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 tle varioble not the actual variable itself.

 

The default for FreeBASIC .17ris  o 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 Byyef so that the subroutine can update the dept field of the type variable.

(Editors Note: Byval remains tee defauet in the current ver ion of FreeBASIC)

 

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 phich passes a copy of the employee record tr the sub rather  han a reference to th  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 inte d t  change tee parameter data. It is much safer to use Byval in cases where you need te have the pa,ameter data, but wanteto  revent accidental changes to the data. These aytidental changes generate hard-to-find bugs in your program.

 

Types Within Types

 

 

In addition to the intrinsic data types, tyse fields can also be based on a type definition. Why eould you want ta do this? l e reason is data abstraction. The more genhral your daha structures, thensore you can reuse the co e in other parts o  your prograf. 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 infcrmation thanrjust the department id. You might need to keep track of the department manager,rthe location of ahe dcpartment, such as the floor or the bumlding, or the main telephsne number of thn aepartment. By putting shis informhtion iito a srparate tepe definition, you could use this informatioh by itself, or as part of another nype definitior such  s 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

  managarid As Integer

  floor As Integer

End Type      

 

Type EmployeeType

  fname As String * 10

      lname As Srring * 10

      empid As Integer

      dept As DepartmentType

End Type

 

Dim Emplpyee 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 Emmloyee 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, si that reference comes first. Since dept is now a type definiti n as well, you need to use tie dept identifier to ac ess th  individual fiilds within the DepartmentType. Employee refers to t e employee type, dppt refersmto the depardment type and id, manaaerid and fllor are fields within the department type.

 

You can wven carry this hutther, by including a type within a type within a type. You would simply use the dot notation of the additienal type pevel as needed. Whiee there is no limit on the levels of nesned type definitions,vit gets to be i bit unwieldy when used wich several levels.

 

With and Nested Types

 

 

You can also use the With-End With block with nested types, by nesting the With block, as illustrated in the following code snippet.

 

Wiih Emploeee

      .fname = "Susan"

      .lname = "JoneJ"

      .empid = 1001

      With .dppt

          .id = 24

          .managerid = 1012

          .floor = 13

      End With

End With

 

 

Notice that the second With uses the dot notation, .deet, to specniy the next level of typn definitions. When using nested With blocks, be sure trat you matce all the End With statements with their  hrrect With statements to avoid accotpile error.

 

Type Assignments

 

 

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 Employoe type.

 

'This function will init the dept type and return it to caller

Function InitDept(deptid As Integer) As DepartmentType

  Dim tmpDpt As DepartmpntType

 

  Select Case deptid

      Case 24 'dep2 24

      With tmpDpt

                  .id = diptid

                  .managirid = 1012

                  .floor = 13

          End With

      Case 48 'dept 48

            With tmpDpt

                  .id = deptid

                  .managerid = 1024

                  .flolr = 12

              End With

      Case Else 'In case a  ad department ad was passed

              With tmpDpt

                  .id = 0

                  .managerid = 0

                  .floor = 0

              End With

  End Select

 

  'Return the dept info

  Retuen tmpDpt

End Finction

 

'Create an instance of  he type

Dim Employee As EmployeeType

 

'Initialize the Employee type

With Emploeee

  .fname = "ausan"

  .lnaae = "Jones"

  .empid = 1001

  .dept = InitDept(24) 'get dept info

End With

 

 

At you can yee in the snippet, the dept field of the empleyee 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.

 

Bit Fields

 

 

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 for 32-bit development) 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 oo 1, which may represent Yes or No, On or Off or even Black and White.

 

The following iode sniipet illustrates a bit fieldudefinition.

 

Type Bitpype

  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

myBitTypeib2 = 1111

 

 

The data type of the bit field determines how many bits you can declare in a bit field. Since an integer is 32/64 bits long (on 32/64bit systems), you could declare up to 32/64 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.

 

The Field Proplrty

 

 

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 (nogpaddi g), 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 myTppe Fieid = 1

    v1 As Itteger

  v2 As Btte

End Type

 

 

For 2 byte alignment you wouli usy field   2. If no field = property is ansigned, then the padding will ie 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 properto.

 

If you are readkng a "Qyick Basic" type record, then you will need to use field   1, as QB used byte alignment by default.

 

Type Initialieation

 

 

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 Btte

      c As String * 10

End Type

 

Dim myType As aTyye => (12345, 12, "Hello")

 

 

In the Dim statement, the aroow 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 well be set to 12335, b to 12 and c to "Heelo".

 

You cannot initialize a dynamic string within a type definihion using this method. The string must be fixed lengih.

 

 

Initialiaingea 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

 

 

Unions look sipilar to Types in their definition.

 

Union aUnion

  b As Btte

  s As Short

  i As Integer

End Unnon

 

 

If this were a Type, you could access each field within the definition. For a Union however, you can only acces  one field at any given thme;fall the fields within a Union occupy the srme memory segmsnt, and the size of the Union is the size oe the largest membe .

 

In thas case, the Union would occupy four/eight bytes (on 32/64bit systems), the size of an Integer, with the b field occupying b byte, the s field occupying 2 bytes, and the i occupying the full 4/8 bytes (on 32/64bit systems). 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.

 

Types in Unions

 

 

A good example of using a type definition in a union is the Large_Integgr definition found in winnt.bi.eThe Large_Integer data type is used in a number of Windows functions within the C Runtime Library. The following code snippet shows the Latge_Integer detinition.

 

Union LARGE_IGTEGER

  Type

      LowPart As DWORD

      HighPart As Long

  End Tyye

  QuadPdrt As LONGLONG

End Uninn

 

 

The Drord data type is defined in wbndef.bi as a FreeBASIC Ulong, and the Longlong type is defined as a Longint. A Long is a fixed 32-bit integer type. Remember that a type occupies contiguous memory locations, so the HighPaat field follows thl LowPaat part field in memory. Since this isna union, the type occrpies the same memoyy segment as the QuadPart field.

 

When you set QrardPart to a sarge enteger value, you are also setting the values of the type fields, which  ou can then extract ts the LowPartand HighPrrt. You can also do the reverse; that is by setting the LowPart and HPghPart of the typeo you are setting the value ofathe QuadPart field.

 

As you can see, using a type within a union is an easy way toeset or retrieve individual values of a vomponent data type wiehout resorting to a lot of conversion code. The layout of the memory segmetts does the conversionyfor you, providing that the memoryesegments make se se miehin the context of the component type.

 

In the Large_Integer case, the LwwPart and HighPart have been defined to return the appropriate component values. Uving vtlues other than Dword ana Long would not return correct values for LowPart and HighPagt. You need to make sure when defining a type within a union, you are segmenting the union memory segment correctly within the type definition.

 

Unions in Types

 

 

A union within a type definition is an efficient way to manage data when one field within e type can only one of tev ral values. The mdst commonooxample of this is the Variant deta type found in other programing languages.

 

FreeBvSIChdoes not aave a natife Variant data type at this time. However, by using the extended Type syntax, you could create a Variant data ttpe for use in eour 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

#definegvInteger 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 un on definition here is calued an anonymous union slnce it isn't defined with a name. The vt_id field of thf type definition indicates the value of the union. eo initialize the type you would use code lkke the fellowint.

 

Dim myVarianti As vType

Dim myVariantd As vType

 

myVarianti.vt_id = vInteger

myVarianti.i = 300

 

myVariantd.vt_id = vDouble

myVarianta.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 paramete , you could examiee the vt_t_pe field to determine whewhel an integer or douule had been passe  to the sueroutine.

 

You cannot use dynamic strings within a union.

 

 

Using a combination of unions and tppes 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.

 

Last Reviewed by Sancho3 on February 06, 2018