Note | This command is only available in the VO and Vulcan dialects |
Declare a data structure and its member names.
[Modifiers] VOSTRUCT <idStructure> [ALIGN 1|2|4|8]
MEMBER <idVarList> AS | IS <idType> [, ...]
MEMBER DIM <ArraySpec> [, ...] AS | IS <idType> [, ...]
[END VOSTRUCT]
Note: The MEMBER statement is shown using two syntax diagrams for convenience. You can declare variables and dimensioned arrays using a single MEMBER statement if each definition is separated by a comma.
Modifiers | An optional list of modifiers that specify the visibility or scope of the entity, such as PUBLIC, STATIC, INTERNAL, EXPORT and UNSAFE. |
<idStructure> | A valid identifier name for the structure. A structure is an entity and shares the same name space as other entities. This means that it is not possible to have a structure and a constant, for example, with the same name. |
MEMBER | Declares one or more structure member variables or dimensioned arrays. You can specify multiple MEMBER declarations on separate lines. |
<idVarList> | A comma-separated list of identifier names for the structure member variables. |
DIM <ArraySpec> | The specification for a dimensioned array to use as a structure member. <ArraySpec> is one of the following: |
<idArray>[<nElements>, <nElements>, <nElements>] |
<idArray>[<nElements>][<nElements>][<nElements>] |
All dimensions except the first are optional. |
<idArray> is a valid identifier name for the array to declare. |
<nElements> defines the number of elements in a particular dimension of an array. The number of dimensions is determined by how many <nElements> arguments you specify. |
<nElements> can be a literal numeric representation or a simple numeric expression involving only operators, literals, and DEFINE constants; however, more complicated expressions (such as function calls) are not allowed. |
AS <idType> | Specifies the data type of the variable you are declaring (called strong typing). For DIM arrays, declares the data type for all array elements. The AS <idType> is required for all structure members. |
Refer to the CLASS entry for a list of valid values for <idType>. Note that the following data types are not supported in structures because they are dynamic types that require garbage collection: |
ARRAY |
FLOAT |
OBJECT |
<idClass> |
STRING |
USUAL |
IS <idType> | Specifies a structure data type in which the memory needed to hold the structure is allocated on the stack (i.e., <idStructure> is the only <idType> allowed with the IS keyword). |
ALIGN 1|2|4|8 | Specifies the memory alignment of the structure. The default alignment is based on the size of the structure members. See the paragraph about alignment below. |
You may want to change this when you need to match a C/C++ structure that has been defined with a different alignment (the #pragma pack in a C/C++ header file). |
. |
AS vs. IS: Once you have defined a structure, you can use its name to declare variables (see GLOBAL and LOCAL statements in this guide) designed to hold instances of a specific structure. When you declare a structure variable, you have the choice of using AS or IS typing. The difference between these two declaration methods is as follows:
• IS automatically allocates the memory needed to hold the structure on the stack and deallocates the memory when the declaring entity returns.
• AS requires that you allocate memory using MemAlloc() when you initialize structure variables. You must also deallocate the memory used by the structure variable using MemFree() before the declaring entity returns.
Important! IS typing is much simpler than AS typing, and in most cases should satisfy your requirements for using structures. AS typing is recommended for experienced systems programmers who can, for various reasons, object to using the stack in this manner.
Allocating substructures: An interesting property of a structure is that it can contain other structures as members but, if you type these substructures using AS, you must allocate and deallocate memory for them. This is true regardless of whether the containing structure is typed with AS or IS:
VOSTRUCT SysOne
MEMBER iAlpha AS INT
MEMBER pszName AS PSZ
VOSTRUCT SysTwo
MEMBER iBeta AS INT
MEMBER strucOne AS SysOne
FUNCTION UseStruct()
LOCAL strucVar AS SysTwo
strucVar := MemAlloc(_SizeOf(SysTwo))
strucVar.strucOne := MemAlloc(_SizeOf(SysOne))
...
MemFree(strucVar.strucOne)
MemFree(strucVar)
To simplify your programming, it makes sense to use IS for declaring substructures. Then, the memory for the substructure will be allocated and deallocated with the memory for its containing structure:
VOSTRUCT SysTwo
MEMBER iBeta AS INT
MEMBER strucOne IS SysOne
FUNCTION UseStruct()
LOCAL strucVar AS SysTwo
strucVar := MemAlloc(_SizeOf(SysTwo))
...
MemFree(strucVar)
Accessing structure members: Structure variables are complex, the components being members that you declare within the structure. To access a structure member, use the dot operator (.) as follows:
<idStructVar>.<idMember>
Where <idStructVar> is a variable name or dimensioned array element that you have previously declared using a structure name, and <idMember> is a variable name or dimensioned array element declared within the VOSTRUCT definition as a MEMBER.
This example illustrates IS structure typing. No allocation is necessary but you must pass the structure by reference to calls.
VOSTRUCT SysOne // Define SysOne data structure
MEMBER iAlpha AS INT
MEMBER pszName AS PSZ
FUNCTION Tester(strucSysOne AS SysOne) AS INT
RETURN strucSysOne.iAlpha
FUNCTION UseStruct()
LOCAL strucVar IS SysOne
strucVar.iAlpha := 100
? Tester(@strucVar)
...
This example illustrates AS structure typing. This requires memory allocation and deallocation:
VOSTRUCT SysOne // Define SysOne data structure
MEMBER iAlpha AS INT
MEMBER pszName AS PSZ
FUNCTION Tester(strucSysOne AS SysOne) AS INT
RETURN strucSysOne.iAlpha
FUNCTION UseStruct()
LOCAL strucVar AS SysOne
strucVar := MemAlloc(_SizeOf(SysOne))
strucVar.iAlpha := 100
? Tester(strucVar)
...
MemFree(strucVar)
With MEMBER, you can list several groups of variable and array names separated by commas and followed by an AS | IS <idType> clause to indicate that all names listed are to be typed as indicated. In this example, the variable x and the dimensioned array z are typed as INT, while the variables ptrX and ptrY are typed as PTR.
VOSTRUCT SysOne // Define SysOne data structure
MEMBER x, DIM z[100] AS INT, ptrX, ptrY AS PTR
You can choose to specify an alignment clause in the structure definition or let XSharp determine the best alignment for you.
The default alignment uses the following mechanism:
•Each member of a size <= 8 gets a memory address inside the structure that is a multiple of its size. So WORD and SHORT members get aligned on EVEN boundaries, DWORD, LONG, PTR, PSZ members get aligned to 4-byte boundaries and REAL8 members get aligned to 8- byte boundaries. Byte members are not aligned, they can appear everywhere in the structure.
•The total size of the structure is aligned to the size of the largest member. This is done to make sure that a dim array of structures (multiple structures adjacent in memory) also align properly
•When a structure contains an sub-structure (an IS declaration) the alignment of the outer structure uses the information from the inner structure.
With manual (explicit) alignment each element of the structure is aligned to a memory address that is a multiple of the alignment specified.
Some examples of automatic alignment
VOSTRUCT test1 // Offset
MEMBER W AS WORD // 0
MEMBER dw AS DWORD // 4
MEMBER b AS BYTE // 8
// Total size of structure = 12 bytes (largest element = 4, so padded to 12)
// Memory layout of structure
// 0123|4567|8901
// WW..|DWDW|B...
//
// WW = Word
// DWDW = Dword
// B = Byte
// . = Padding
VOSTRUCT test1 // Offset
MEMBER W AS WORD // 0
MEMBER r8 AS REAL8 // 8
MEMBER b AS BYTE // 16
// Total size of structure = 24 bytes (largest element = 8, so padded to 24)
// Memory layout of structure
// 01234567|89012345|67890123
// WW......|R8R8R8R8|B.......
In some situations you need to match a structure declaration from a C/C++ header file that has explicit alignment. Then you need to add the ALIGN clause to your structure declaration.
This forces the compiler to align the structure elements to a multiple of the specified size.
An alignment of 1 tells the compiler NOT to use padding but to align all elements of a structure next to eachother. This is the most compact, but may be slower.
Some examples of explicit alignment
VOSTRUCT test1 ALIGN 1 // Offset
MEMBER W AS WORD // 0
MEMBER r8 AS DWORD // 2
MEMBER b AS BYTE // 6
// Total size of structure = 7 bytes (multiple of 1)
// Memory layout of structure
// 01|2345|6
// WW|DWDW|B
//
// WW = Word
// DWDW = Dword
// B = Byte
// . = Padding
VOSTRUCT test1 ALIGN 2 // Offset
MEMBER W AS WORD // 0
MEMBER dw AS DWORD // 2
MEMBER b AS BYTE // 6
// Total size of structure = 8 bytes ( multiple of 2)
// Memory layout of structure
// 01|23|45|67
// WW|DW|DW|B.
VOSTRUCT test1 ALIGN 4 // Offset
MEMBER W AS WORD // 0
MEMBER r8 AS DWORD // 4
MEMBER b AS BYTE // 8
// Total size of structure = 12 bytes (multiple of 4)
// Memory layout of structure
// 0123|4567|8901
// WW..|DWDW|B...
VOSTRUCT test1 ALIGN 8 // Offset
MEMBER W AS WORD // 0
MEMBER r8 AS DWORD // 8
MEMBER b AS BYTE // 16
// Total size of structure = 24 bytes (multiple of 8)
// Memory layout of structure
// 01234567|89012345|67890123
// WW......|DWDW....|B.......
GLOBAL, LOCAL, MemAlloc(), MemFree(),, UNION