Type

Declares a user-defined type.

Syntax
   Type typename
      fieldname1 As DataType
      fieldname2 As DataType
      As DataType fieldname3, fieldname4
      ...
   End Type

   Type typename [Alias "alternatename"] [Extends base_typename] [Field = 
   alignment]
      [Private:|Public:|Protected:]

      Declare Sub|Function|Constructor|Destructor|Property|Operator ...
      Static variablename As DataType
      ReDim arrayname(array dimensions) As DataType

      fieldname As DataType [= initializer]
      fieldname(array dimensions) As DataType [= initializer]
      fieldname(Any [, Any...]) As DataType
      fieldname : bits As DataType [= initializer]

      As DataType fieldname [= initializer], ...
      As DataType fieldname(array dimensions) [= initializer], ...
      As DataType fieldname(Any [, Any...])
      As DataType fieldname : bits [= initializer], ...

      Union
         fieldname As DataType
         Type
            fieldname As DataType
            ...
         End Type
         ...
      End Union

      Type typename2 ...
         ...
      End Type

      ...
   End Type

Description
   Type is used to declare custom data types containing one or more data 
   fields, including integer types, floating point types, fixed-size or 
   variable-length (dynamic) arrays, fixed-size or variable-length strings, 
   bitfields, or other user-defined types.

   Types support various functionality related to object-oriented 
   programming:
      * Inheritance through the use of the Extends keyword.
      * Member procedures such as Subs or Functions, including Abstract or 
        Virtual ones.
      * Member procedures with special semantic meaning such as 
        Constructors or a Destructor.
      * Static member variables.
      * Member visibility specifiers: Public:, Private:, Protected:.

   A Type can also contain nested Types or Unions, of different kinds:
      * Nested Anonymous Type/Union:
            - Nested Anonymous Type/Union allows data members to be grouped 
            as desired.
            - Anonymous Type or Anonymous Union can be nested on condition 
            of alternating their nesting.
            - Nested Anonymous Type/Union can not have procedure members or 
            static data members (same restriction than for a local scope 
            named Type/Union).
      * Nested Named Type/Union:
            - Nested Named Type/Union allows declaration of an inner 
            structure inside a (named) Type/Union namespace and according 
            to the access right of the place
            - Mostly everything it can be done in a Named Type/Union can 
            also be done in a Nested Named Type/Union.
            - When there are circular dependencies between Types/Unions, 
            Nested Named Type/Union usage can avoid using type aliases and 
            forward referencing.
      * Nested Type-Def:
            - Nested Type-Def allows declaration of an inner Type (Alias) 
            inside a Type/Union and according to the access right of the 
            place.
   The main structure (Type/Union) must be always named, the other (nested) 
   structures can be anonymous or named.

   Alias "alternatename" specifies that if typename must be encoded 
   (mangled) in to a public symbol (as in an object module or library), 
   then specifically use alternate name instead of the usual encoding 
   (mangling) of typename.

   Memory layout
   Types lay out their fields consecutively in memory, following the native 
   alignment and padding rules (described on the Field page). Special care 
   must be taken when using Types for file I/O or interacting with other 
   programs or programming languages, in case the alignment and padding 
   rules are different. The optional Field = number specifier can be used 
   to change the behavior on the FreeBASIC side.

   Variable-length data
   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, Types may contain variable-length (dynamic) string 
   or array data members. 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 variable-length string/array data. For sizing the 
   structure of the array descriptor in the Type, a variable-length 
   (dynamic) array data member must be always declared by using Any(S) in 
   place of the array bounds, in order to fix the amount of dimensions 
   based on the number of Anys specified. A variable-length (dynamic) array 
   data member can also be pre-sized in its declaration by using syntax 
   with ReDim.
   Variable-length array fields are considered as pseudo-objects when they 
   are declared in a Type, just like variable-length strings (the implicit 
   copy constructor and the implicit let operator themselves support 
   [re]sizing and copying such arrays, or their erasing).

   Because of that, saving such a Type into a file will write out the 
   descriptor, not the actual string/array data. In order to embed 
   strings/arrays into Types directly, fixed-length strings/arrays must be 
   used.

   Similarly, when maintaining dynamic data manually through the use of 
   pointers within a Type, it does usually not make sense to save the Type 
   to a file, because the address stored in the pointer field will be 
   written to file, not the actual memory it points to. Addresses are 
   meaningful to a specific process only though, and cannot be shared that 
   way.

   Special note on fixed-length strings
   Currently, fixed-length string fields of String * N type have an extra 
   null terminator at their end, for compatibility with C strings, making 
   them incompatible with QB strings inside Types, because they actually 
   use up N+1 bytes, instead of just N bytes. A possible work-around is to 
   declare the field As String * (N-1), though this will not work in future 
   releases if the null terminator is removed.  Another alternative is to 
   use a Byte or UByte array with the proper size.

   Note on bitfields ( fieldname : bits )
   Bitfields can only be declared inside a type or a union, and allow to 
   specify some very small objects of a given number of bits in length. 
   Each field is accessed and manipulated as if it were an ordinary member 
   of the structure.
   Only integer data-types (up to 32-bit for 32-bit development or 64-bit 
   for 64-bit development) are valid. The sizes of the declared data-types, 
   large enough to contain the bit patterns, affect how the bitfields are 
   placed in memory.
   Bitfield members in a type are packed together, unless the next member 
   is a non-bitfield (nested union is considered a non-bitfield).
   A bitfield does not have any address (one cannot get a pointer to it and 
   its offset inside the structure).

Example
   This is an example of a QB-style type, not including procedure 
   definitions
   Type clr
      red As UByte
      green As UByte
      blue As UByte
   End Type

   Dim c As clr
   c.red = 255
   c.green = 128
   c.blue = 64

   And this is an example of a type working as an object:
   '' Example showing the problems with fixed length string fields in UDTs
   '' Suppose we have read a GIF header from a file
   ''                        signature         width        height
   Dim As ZString*(10+1) z => "GIF89a" + MKShort(10) + MKShort(11)

   Print "Using fixed-length string"

   Type hdr1 Field = 1
      As String*(6-1) sig /' We have to dimension the string with 1 char
   						'  less to avoid misalignments '/
      As UShort wid, hei
   End Type

   Dim As hdr1 Ptr h1 = CPtr(hdr1 Ptr, @z)
   Print h1->sig, h1->wid, h1->hei '' Prints GIF89 (misses a char!)  10  11

   '' We can do comparisons only with the 5 visible chars and creating a temporary string with LEFT

   If Left(h1->sig, 5) = "GIF89" Then Print "ok" Else Print "error"

   '' Using a ubyte array, we need an auxiliary function to convert it to a string
   Function ub2str( ub() As UByte ) As String
      Dim As String res = Space(UBound(ub) - LBound(ub) + 1)
      For i As Integer = LBound(ub) To UBound(ub)
         res[i - LBound(ub)] = ub(i)
      Next
      Function = res
   End Function

   Print
   Print "Using an array of ubytes"

   Type hdr2 Field = 1
      sig(0 To 6-1) As UByte '' Dimension 6
      As UShort wid, hei
   End Type

   Dim As hdr2 Ptr h2 = CPtr(hdr2 Ptr, @z)
   '' Viewing and comparing is correct but a conversion to string is required

   Print ub2str(h2->sig()), h2->wid, h2->hei '' Prints GIF89a  10  11 (ok)
   If ub2str(h2->sig()) = "GIF89a" Then Print "ok" Else Print "error" '' Prints ok

   This is an example of conversion from an Ubyte to a digit string in base 
   8 (octal string), by using bitfields in a local UDT (conversion 
   equivalent to 'Oct(x, 3)'):
   Function UbyteToOctalString (ByVal b As UByte) As String
    
      Union UbyteOctal
         number As UByte
         Type
            d0 : 3 As UByte
            d1 : 3 As UByte
            d2 : 2 As UByte
         End Type
      End Union
    
      Dim uo As UbyteOctal
      uo.number = b
      Return uo.d2 & uo.d1 & uo.d0
    
   End Function

   For I As Integer = 0 To 255
      Print Using "###: "; I;
   ''    Print Oct(I, 3),
      Print UbyteToOctalString(I),  '' this line is thus equivalent to the previous one
   Next I
   Print

   Sleep

   This is an example with Nested Named Type:
   Type Parent
      Private:
         Dim As String nameParent
         Declare Constructor()
         Declare Constructor(ByRef As Parent)
         Type Child
            Dim As String nameChild
            Dim As Parent Ptr ptrParent
            Declare Sub kinship()
         End Type
         Dim As Child listChild(Any)
      Public:
         Declare Constructor(ByRef _nameParent As String)
         Declare Sub addChild(ByRef _nameChild As String)
         Declare Sub kinship()
   End Type

   Constructor Parent(ByRef _nameParent As String)
      This.nameParent = _nameParent
   End Constructor

   Sub Parent.addChild(ByRef _nameChild As String)
      ReDim Preserve This.listChild(UBound(This.listChild) + 1)
      This.listChild(UBound(This.listChild)).nameChild = _nameChild
      This.listChild(UBound(This.listChild)).ptrParent = @This
   End Sub

   Sub Parent.Child.kinship()
      Print "'" & This.nameChild & "'" & " is child of " & "'" & This.ptrParent->nameParent & "'"
   End Sub

   Sub Parent.kinship()
      For i As Integer = 0 To UBound(This.listChild)
         This.listChild(i).kinship()
      Next i
   End Sub

   Dim As Parent p = Parent("Kennedy")
   p.addChild("John Jr.")
   p.addChild("Caroline")
   p.addChild("Patrick")
   p.kinship()

   Sleep

Version
   * Since fbc 1.10.0: Nested Named Type/Union capability added.

Platform Differences
   * The default Field alignment parameter is 4 bytes for DOS and Linux 
     targets.
   * The default Field alignment parameter is 8 bytes for Windows targets 
     (this difference with regard to 4 bytes applies only to Longint and 
     Double members).

Dialect Differences
   * Object-related features such as functions declared inside Type blocks 
     are supported only with the -lang fb dialect since version 0.17b
   * In the -lang fb and -lang fblite dialects, the default Field 
     alignment parameter depends on the target platform.
   * With the -lang qb dialect the fields are aligned to byte boundaries 
     by default, unless otherwise specified.
   * To force byte alignment use FIELD=1.

Differences from QB
   * At present, fixed-length strings have an extra, redundant character 
     on the end, which means they take up one more byte than they do in QB. 
     For this reason, UDTs that use them are not compatible with QB when 
     used for file I/O.

See also
   * Type (Alias)
   * Type (Temporary)
   * Union
   * Enum
   * TypeOf
   * OffsetOf
   * Alias (Name)
   * Field
   * Extends
   * Extends Zstring
   * Extends Wstring
   * With
   * Standard Data Type Limits
   * Coercion and Conversion

