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. Microsoft Visual Basic uses an alignment style equivalent to DWORD FILL. | |||
|
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, ASCIIZ
strings, and fixed-length strings.
UDT member arrays are not resizable at runtime. Additionally, the
| |||
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 or Function 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) ... 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:
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 ... 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, ASCIIZ 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, SIZEOF, TYPE SET, UNION/END UNION, User-Defined Types, Unions | |||
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 ... |