TYPE/END TYPE block

Purpose

Define a User-Defined Data Type (UDT), containing one or more member elements.

Syntax

TYPE MyType [BYTE | WORD | DWORD | QWORD] [FILL]

  [MemberName [(subscripts)] AS] TypeName

  [MemberName [(subscripts)] AS TypeName]

  [...]

END TYPE

Remarks

The TYPE statement has the following parts:

TYPE

The beginning of a User-Defined Type definition.

MyType

The name of the User-Defined Type, which must conform to standard variable naming conventions.

 

Member alignment

 

TYPE definitions may optionally specify an alignment of BYTE (the default), WORD, DWORD, or QWORD, as well as FILL characteristics.  With standard alignment, each member of a Type Structure will be located on the specified boundary.  For example, with DWORD, up to 3 bytes may be skipped between members to accomplish alignment.

However, when a user-defined type is defined as a member of a larger user-defined type, this "sub-type" retains its original size and alignment, just as first declared.

BYTE

Each member will be aligned on a BYTE boundary - no padding or alignment is applied to the structure.  This is the default alignment method.

WORD

Each member will be aligned on a WORD boundary.  Any odd byte between members of TYPE will be automatically skipped and ignored.  The UDT structure may also be padded with one trailing byte to ensure the total structure size is a multiple of 2 bytes.

DWORD

Each member will be aligned on a DWORD boundary.  Up to three bytes will be skipped to accomplish this alignment.  The UDT structure is also padded with up to three trailing bytes to ensure the total structure size is a multiple of 4 bytes.

QWORD

QWORD alignment is included for compatibility with Windows, it cannot be fully implemented in a 32-bit operating system.  With QWORD, individual members are 64-bit aligned for the appropriate structure size, but variables of that type may only be aligned on 32-bit boundaries, as stack pointer alignment is not guaranteed.

FILL

If the FILL option is specified, such as TYPE xxx DWORD FILL, the following rules apply:

1.        No bytes are skipped if the next member of the Type will fit entirely into that space to be skipped.

2.        Fixed-length strings are considered to be an array of bytes, so no bytes are skipped preceding them.

3.        The total size of an array is considered to determine if FILL should affect its placement within the structure.  For example, with DWORD FILL, an array of two integers would be started on a 4-byte boundary, even if two or three bytes must be skipped.

 

Type members

MemberName

The name of a member of the User-Defined Type.  This too must follow the standard variable naming conventions.

subscripts

The dimensions of a member array.  Arrays of one and two dimensions are supported, but must be defined with constant or numeric literal values.  That is, the total size of a UDT must be known at compile-time, so items like dynamic strings, which vary in size, cannot be part of a TYPE structure.  A STRING PTR can, however, since a pointer is implemented as a DWORD.

Like conventional arrays, the default lower array boundary is zero, but positive non-zero values may be used to specify a specific range of subscript index values for the array, separated from the upper array boundary subscript with the TO keyword.  Additionally, both the lower and upper subscript index values must be zero or greater (ie, negative subscript values are not permitted).  Examples of valid syntax follow:

TYPE MYTYPE

  id AS INTEGER              ' Scalar UDT member

  Styles(6)         AS DWORD '  7 elements (0 TO 6)

  Yrs(1980 TO 2010) AS LONG  ' 31 elements

  Team(100 TO 101)  AS BYTE  '  2 elements

  Rating(1 TO 10)   AS DWORD ' 10 elements

  X(1 TO 5, 0 TO 5) AS EXT   ' 30 elements (5x6)

  Y(4,3)            AS QUAD  ' 20 elements (5x4)

END TYPE

Individual UDT structures can be up to 16 MB each.  A single member element of a UDT may also occupy the entire 16 MB.  For example, arrays within a UDT, nul-terminated strings, and fixed-length strings.  UDT member arrays are not resizable at runtime.  Additionally, the ARRAY statements cannot be used directly on a UDT member array.  Instead, use DIM..AT to declare a conventional array at the same memory address as the UDT member array, and the ARRAY statement can then be used on that array.

TypeName

One of the supported data types, including User-Defined Types and Unions, with the exception of arrays.

END TYPE

Marks the end of the User-Defined Type definition.

It is often very convenient to be able to refer to several different types of things as a single unit or data structure.  For example, in an accounting program, an account number and amount are part of what makes up a single journal entry.  The TYPE/END TYPE block statements make it easy to create a single UDT that holds such information.

TYPE JournalType DWORD   ' type name and alignment

  AccountNumber AS LONG  ' member name and data type

  Amount        AS CUR   ' this is another one      

END TYPE                 ' end of type declaration

 

DIM JournalEntry AS JournalType ' declare a record

TYPE/END TYPE blocks must be defined outside of a Sub, Function, or Class and may be defined only once in any program.  It is usually easiest to put your TYPE/END TYPE block definitions in an Include file and use the #INCLUDE metastatement in any module that may need to use them.

TYPE/END TYPE blocks do not declare any variables; instead, they simply define a new type.  You can declare variables of that type using the DIM or REDIM statements, or any statement that lets you use an AS clause:

DIM TypeVariable as TypeVariableType

Once you have a User-Defined Type variable declared, you can access its member elements using the following format:

TypeVariable.Element

For example, to change the account number in the JournalEntryType type, you might use a statement like:

JournalEntry.AccountNumber = 1000

A User-Defined Type can be used like any built-in PowerBASIC type.  For example, you can define an array of record variables:

DIM JournalEntries(1 TO 100) AS JournalEntryType

…or even create a procedure that accepts a record variable:

SUB PrintJournalEntry(aJournalEntry AS JournalEntryType)

  ' Print journal

END SUB

You can also use pointers in a TYPE definition. Note that the first member in the next example is auto-aligned to start on a DWORD boundary, and three bytes are skipped so that the second member is also aligned on a DWORD boundary:

TYPE MyType DWORD

  Count AS BYTE    ' Aligned to a DWORD boundary

  y AS INTEGER PTR ' Aligned to next DWORD boundary

  z AS STRING PTR

END TYPE

Since pointers are stored as a DWORD, their length is 4 bytes when used in a TYPE/END TYPE, regardless of the length of their target.  To access the target of a pointer, you must place the at-sign in front of the TYPE/END TYPE member, not the name of the TYPE itself:

iResult% = @MyType.y    ' Invalid

iResult% = MyType.@y    ' Valid

You can also declare a variable that is a pointer to a TYPE:

TYPE MyData

  Val1 AS INTEGER

  Val2 AS INTEGER

  Val3 AS INTEGER

  Val4 AS INTEGER

END TYPE

 

DIM Info AS MyData PTR

Info = VARPTR(YourData)

Message$ = HEX$(@Info.Val1) + $CRLF + _

           HEX$(@Info.Val2) + $CRLF + _

           HEX$(@Info.Val3) + $CRLF + _

           HEX$(@Info.Val4)

Note that the target specifier is in front of the TYPE name since it is the pointer.  Val1, Val2, Val3, and Val4 represent offsets from that pointer.  PowerBASIC does support a pointer within a structure pointer, but you should be very careful in their use.  Changing the structure pointer itself could make all member pointers invalid.  See the topic on pointers for more information.

 

Bit Variables

 

TYPE structures may contain bit variables, which are named BIT (unsigned values) or SBIT (signed values).  Each bit variable may occupy from 1 to 31 bits, and they may be packed one after another up to a total of 32 bits per bit field.  The size of a bit variable is defined as follows:
 

var AS BIT * nlit [IN BYTE|WORD|DWORD]

…where the term "* nlit" defines the number of bits (1 to 31), and the optional term "IN BYTE|WORD|DWORD", if present, defines the start of a new bit field of 1, 2, or 4 bytes.  For example:

TYPE ABCD

  Valu2 AS BIT  * 31 IN DWORD

  Sign1 AS SBIT * 1

  nybl2 AS BIT  * 4 IN BYTE

  nybl1 AS BIT  * 4

END TYPE

The example TYPE structure above is 5 bytes in size, containing a 4-byte bit field and a 1-byte bit field.  In this case, each contains two bit-variables of varying size.  The range of values which may be stored depends upon the number of bits available.  For example, "BIT * 4" has a range of 0 to 15, "SBIT * 1" has a range of -1 to 0, and "SBIT * 5" has a range of -16 to +15.

 

Structures within structures

 

Structures (TYPE/UNION) may be embedded within another structure, for simplification in referencing deeply nested items, by simply stating the structure name alone at the appropriate position.  The internal alignment of the member structure is precisely maintained regardless of other alignment specifications, to foster inheritance issues.  For example:
 

TYPE ABCD3   

A AS LONG    

ABCD2

C AS LONG    

END TYPE  

TYPE ABCD2     

D AS DWORD    

E AS DOUBLE   

ABCD1         

END TYPE  

UNION ABCD1

F AS DWORD

G AS LONG

H AS SINGLE

END UNION

In this case, you could access the lone Single-precision float member of this structure very simply.  Assuming DIM X AS ABCD3, you could reference the Single-precision Union member with the statement X.H, instead of the extended syntax X.ABCD2.ABCD1.H

For related information, please refer to the UNION/END UNION and User-Defined  Types and Unions sections.

Restrictions

When measuring the size of a padded (aligned) UDT structure with the LEN or SIZEOF statements, the measured length includes any padding that was added to the structure.  For example, the following UDT structure:

TYPE LengthTestType DWORD

  a AS INTEGER

END TYPE

' more code here

DIM abc AS LengthTestType

x& = LEN(abc)

Returns a length of 4 bytes in x&, since the UDT was padded with 2 additional bytes to enforce DWORD alignment.  Note that the LEN and SIZEOF of individual UDT members will return the true size of the member without regard to padding or alignment.  In the previous example, LEN(abc.a) returns 2.

Individual UDT structures can be up to 16 MB each.  Arrays within a UDT, nul-terminated strings and fixed-length strings may occupy the full 16 MB structure size limit.

Field strings and dynamic strings cannot be used in UDT or UNION structures.  Attempting to do so results in a compile-time Error 485 ("Dynamic/Field strings not allowed").

See also

DIM, LEN, REDIM, LET (with Types), SIZEOF, TYPE SET, UNION/END UNION, User-Defined TypesUnions

Example

TYPE JournalEntryType

  AccountName    AS STRING * 20

  AccountNumber  AS LONG

  Amount         AS CUR

END TYPE

 

DIM JournalEntry AS JournalEntryType

 

JournalEntry.AccountName   = "Joe Smith"

JournalEntry.AccountNumber = 7467047&

JournalEntry.Amount        = 42.01@

' process journal entry here

JournalEntry.AccountNumber = 705233476&

JournalEntry.Amount        = 69.35@

' process journal entry here