See Also: Declaring Variables: Array Declarations, Move, Struct Type, Struct Variables, Struct Declaration, Struct Variable Assignments, Struct Properties, SizeOfType
Declares a new structured data type.
Struct {type-name}
{data-type} {member-name}
[{data-type} {member-name}]
[…]
End_Struct
or
Struct {type-name}
[ {Name={alternate-member-name} } ]
{data-type} {member-name}
[{data-type} {member-name}]
[…]
End_Struct
Where:
{type-name} is the name of the new struct type. Struct type names must be unique symbols. DataFlex naming convention suggests prefixing struct type names with a lowercase 't'.
{data-type} is the data type of individual struct members. This can be any DataFlex simple type (Integer, Number, Real, Boolean, Date, String, Pointer, Handle), an array type, or another struct type.
{member-name} is the name assigned to each struct member. Struct member names must be unique within the declared struct. i.e. a struct cannot declare two members with the same name. Member names do not have to be unique outside the struct declaration. You may not use a DataFlex reserved word as a member name.
{alternate-type-name} is an optional Name meta-data tag that can be used to designate an alternate name to Struct members that is used when serializing and deserializing structs to and from JSON and XML. See Name meta-data tag for more information.
A struct represents a set of elements where each element is called a member. A struct type declaration defines a particular set of elements and associates it with a new type identifier.
Once a struct type is declared it can be used just like any other DataFlex data type. i.e. you may declare and use variables and properties, and you may declare procedure or function parameters and return types of a struct type. For example…
Struct tCreditCard // declares a new struct type
Integer iType
String sName
Integer iNumber
Date dExpires
End_Struct
tCreditCard MyCreditCard // declares a new variable of tCreditCard type
Members of a struct variable are accessed using the member access operator (the dot operator). For example…
Move 0 to MyCreditCard.iType
Move "Joe Bloggs" to MyCreditCard.sName
Move 1234123412341234 to MyCreditCard.iNumber
Date dToday
Sysdate dToday
If (MyCreditCard.dExpires >= dToday) Showln "Expired!"
For each struct type defined, the compiler automatically defines the symbol _struct_{name} where {name} is replaced with the name of the struct type. This can be used at compile time to determine whether a particular struct type has been defined. For example:
#IFDEF _struct_tCreditCard
// Do something special if the tCreditCard struct type has been defined
...
#ENDIF
For more information refer to struct types in the Language Guide.
Structure alignment (or structure padding) happens when structs are passed to other DLLs or Windows functions. This is because the compiler ensures that each struct instance will have the alignment of its widest scalar member (for performance reasons) and extra memory space may be inserted within the struct (unless structure alignment is explicitly switched off in the DLL).
DataFlex does not do structure alignment. This means that you might have to add extra padding items yourself to exposed Windows structs. This issue is especially relevant to the 64-bit platform. Take this example:
Struct tWinChooseFont
DWord lStructSize
Handle hwndOwner
End_Struct
In 32-bit, both DWord and Handle are 32-bit items (4 bytes), which does not lead to any padding. However, in 64-bit, Handle has become 64-bit (8 bytes) and that causes the struct to have 8-byte alignment, which means that in Windows compilers there will be 4 bytes of space inserted after lStructSize. If this doesn’t get corrected in 64-bit environments, there can be an unexpected runtime error or crash upon calling the external function. The solution in DataFlex code is:
Struct tWinChooseFont
DWord lStructSize
#IFDEF IS$WIN64
Integer iStructAlignment
#ENDIF
Handle hwndOwner
End_Struct
Be aware that such changes might influence code where you do a SizeOfType() on that struct.
The structure alignment issue is not relevant to structs internal to your application (not passed to outside DLLs) or structs in COM class interfaces.
The following example demonstrates a nested struct declaration. i.e. a struct type with a member that is also a struct type.
Struct tAddress
String Street
String City
String State
Integer Zip
End_Struct
Struct tCompany
String Name
tAddress PostalAddress
tAddress Location
Integer Phone
End_Struct
The following example declares a struct variable named tStudent, then creates a dynamic array of type tStudent named myStudents. Next, a loop is created that finds Student records and fills the array with the information from the database.
// declare data type tStudent
Struct tStudent
String FirstName
String LastName
String ClassName
Integer LastTestScore
End_Struct
tStudent[] myStudents // dynamic array of tStudent
open Student // open Student table
integer i
for i from 0 to iNumberOfStudents
clear Student
move i to Student.Id
find eq Student by 1 // Id
if (Found) begin
move Student.First to myStudents[i].FirstName
move Student.Last to myStudents[i].LastName
move Student.Class to myStudents[i].ClassName
move Student.TestScore to myStudents[i].LastTestScore
end
loop
Initializing struct variable members:
In the example below, move is used to initialize members of the myBillingAddress struct variable.
struct tUSAddress
string sFirstName
string sLastName
string sAddressLine1
string sAddressLine2
string sCity
string sState
integer iZipCode
end_struct
Procedure CreateBillingAddress
tUSAddress myBillingAddress
// initialize myBillingAddress
move "John" to myBillingAddress.sFirstName
move "Smith" to myBillingAddress.sLastName
move "Data Access Worldwide" to myBillingAddress.sAddressLine1
move "14000 SW 119 Ave" to myBillingAddress.sAddressLine2
move "Miami" to myBillingAddress.sCity
move "FL" to myBillingAddress.sState
move "33186" to myBillingAddress.iZipCode
End_Procedure
Copying struct variable members:
Assume that the example below is a continuation of the sample above, using the same struct declaration for tUSAddress. Here, move is used to copy the value of myBillingAddress to myShippingAddress. Using move to copy struct variables uses a deep memberwise copy operation to copy each struct variable member value from the first struct variable to the second struct variable.
Procedure CreateShippingAddress tUSAddress myBillingAddress
tUSAddress myShippingAddress
move myBillingAddress to myShippingAddress
End_Procedure
Re-initializing struct variable members:
There is a quick way to re-initialize array variables to their original (blank) values. Simply move an un-initialized array variable of the same type:
Struct tTest
Integer iTest
String sTest
End_Struct
Procedure Test
tTest localTest blankTest
// initialize localTest with values
Move 104 to localTest.iTest
Move "Fred" to localTest.sTest
// re-initialize localTest elements to their original (blank) values
Move blankTest to localTest
End_Procedure
The same can be done with struct properties:
Property tTest pMyTest
Procedure ReinitializepMyTest
tTest blankTest
Set pMyTest to blankTest
End_Procedure
You may not declare a member whose type is the same as the struct type being declared (i.e. recursive struct declarations are not allowed).
When declaring a static array struct member, the array dimension must be a constant value computed at compile time. For example the declaration below is correct.
Struct tContact
String Company
String[5] ContactNames
Integer Fax
End_Struct
However the following declaration will produce a compile-time error.
Integer icNames
Move 5 to icNames
Struct tContact
String Company
String[icNames] ContactNames
Integer Fax
End_Struct
This restriction does not apply to dynamic or jagged array struct members.
To see how to declare arrays of structs, see Declaring Variables: Array Declarations.