Defining a field
A field defines a location in memory. A field can either be an unnamed filler that merely occupies space or a named variable that holds a data value to be processed by your program. You can use fillers when a record has many fields, but a routine uses only a small subset of the fields. Only the used fields must be named; many developers combine adjacent unreferenced fields into single filler fields to shorten their record definitions. (Note, however, that the side effect of creating merged filler fields is that your entire record structure is not fully documented in the program listing.)
For all types in traditional Synergy and for descriptor types in Synergy .NET, named records, named commons, named literals, groups in unnamed records, structures, fields, static array elements, and subscripts of memory handles cannot be larger than 65,535 bytes on 32-bit systems. You can specify the same field name in more than one named record or class, but you must reference that field with a unique path specification. (See Variable path specifications for more information.)
For example:
record consignee name ,a40 . . . record consigner name ,a40 . . . proc consignee.name = "Jones" consigner.name = "Smith"
Fields have several characteristics: access type, name, size, type, position within the record, initial value, and number of optional array elements. Each field definition specifies the characteristics of a single field, and the entire collection of field definitions comprises the record’s data layout.
A field definition has the following format:
{attribute[(value_list)]}
[access] [field_mod] [name], [dimension|[dim, …]]type[size] [@position][, init_value, …]
Field definition components
attribute
(optional) An attribute that inherits from System.Attribute with an attribute target of All or Field. Synergy .NET only. For more information see Attributes (.NET).
value_list
(optional) The values for the specified attribute. Synergy .NET only. For more information see Attributes (.NET).
access
(optional) For a class field only, one of the following access modifiers:
Access is not restricted. This is the most accessible option.
Access is limited to the containing class or types derived from the containing class.
Access is limited to the containing type. This is the least accessible option. (default)
Access is limited to the current assembly. (Synergy .NET only)
Access is limited to the current assembly and types derived from the containing class. (Synergy .NET only)
field_mod
(optional) For a class field only, one of the following field modifiers (with the exception that READONLY and STATIC can be specified together).
A static field whose value is not changed once it is initialized. CONST implies STATIC.
The field’s value can only be set during declaration or in the constructor of the class.
The field must be initialized. See Requiring an initial value for more information. (Synergy .NET only)
The field’s value can be accessed (read and write) even if the class that contains the field hasn’t been instantiated.
name
(optional) A name used to reference a field’s data space. If the field is a member of an unnamed class record, a field name must be specified. If you don’t specify name, you can still reference the data space by ranging the record variable of which the field is a part. If you don’t specify a record name either, the field’s contents won’t be directly accessible. (See Ranged references and Relative ranging for more information.)
dimension
(optional) Indicates the number of times the field occurs. The default value is 1. The presence of dimension indicates a pseudo array. (Pseudo arrays are deprecated. We recommend you use real arrays instead.) See Defining arrays for more information.
dim
(optional) Indicates the number of times the field occurs. The presence of dim indicates a real or dynamic array. You must type the brackets around the dimension elements; they do not mean that the arguments are optional. The brackets must contain at least one element.
- For real arrays, dim is one or more decimal expressions, separated by commas, that indicate the number of dimensions.
- For dynamic arrays, dim is one or more # characters, separated by commas, that indicate the number of dimensions. For example, a dynamic array of three dimensions would be specified as [#,#,#]. The actual sizes are specified using the NEW keyword on a System.Array at runtime.
See Defining arrays for more information on defining real and dynamic arrays.
One of the following data types. (For more information on these types see the Data Types table; there are detailed descriptions of each type following the table.)
a
boolean
byte
d, d.
D_ADDR
decimal
double (Synergy .NET only)
float (Synergy .NET only)
i
int
long
p, p. (traditional Synergy only)
sbyte
short
string
ushort (Synergy .NET only)
uint (Synergy .NET only)
ulong (Synergy .NET only)
@*
@class
@interface (Synergy .NET only)
@delegate (Synergy .NET only)
enumeration
structure
T (Synergy .NET only)
[#,#]value
[#,#]@value
[#,#]@class
[#,#]@*
You can also use any System.xxx type for type.
size
(optional) For nonclass types, an expression that indicates the number of characters in each occurrence of the field.
If type is d. or p., the size must be specified in the form size.places, where size is the length of the entire field and places is the number of digits to the right of the decimal point. For example, d5.3 is an implied-decimal field that is five digits long, with the last three digits to the right of the decimal point. Size cannot be greater than 28 for d. or 18 for p.. Places cannot be greater than 28 for d. or 10 for p., or greater than the value of size.
If type is i, size can only have a value of 1, 2, 4, or 8.
If type is d, size cannot be greater than 28.
If type is p, size cannot be greater than 18.
Size is optional and can be an asterisk (*) if you specify an initial value list. The size is the size of the largest initial value specified. See Field size for more information.
(optional) For nonclass types, the position of the field either as an absolute character position or relative to the beginning of the current record. The position specification is described in more detail in Specifying a field position indicator.
init_value
(optional) For nonclass types, an initial value to be assigned to successive occurrences of the field. The init_value specification is described in more detail in Initial values.
Field size
If you’ve specified a dimension for a nonclass field, the size must be a positive decimal literal. You can either omit the size or assign a size of “*” to indicate that the size of the field should be the size of the initial value assigned to that field. (If you specify a size of “*”, make sure that you also assign an initial value. If you don’t, you’ll get a syntax error.) This feature is most useful for fields that hold strings of undetermined size (such as title information), because you don’t have to count characters—the compiler counts the characters for you.
An i4 variable is more efficient than i1 or i2. You can also use the compiler directive .ALIGN LONG before an i4 variable to increase its efficiency. |
Throughout this section, wherever we refer to a decimal expression, you can specify any expression that can be completely evaluated at compile time. (See Compile-time expressions and optimizations for details.) When using decimal expressions, you may need to separate one syntax element from another using a space or parentheses so the compiler knows how to interpret each element correctly. For example, if you specify the data type and size as follows:
d2*^size(fld)
the compilation fails with a syntax error, because the compiler tries to interpret the number 2 as the field size.
However, if you include a space or parentheses, the compiler knows that 2*^size(fld) is an expression representing the field size. For example,
d 2*^size(fld)
or
d(2*^size(fld))
Defining arrays
Often a particular field must occur several times within a record structure, with each occurrence representing a separate data element. Any field that is defined to occur more than once is called an array. The data area allocated to an array is the product of the number of elements defined and the size of a single element. For example, a six-character field defined as having 10 elements is allocated a total of 60 characters of space in memory (10 x 6).
You can define an array as either pseudo (without brackets), real (with brackets and implicitly created at compile time), or dynamic (with brackets and explicitly created dynamically at runtime). Both pseudo and real arrays are also referred to as static arrays.
All array elements are initially set to their default value based on data type. See the Data Types table for a list of default values.
Static (pseudo and real) arrays
A pseudo array is a one-dimensional array of type a, d, d., i1, i2, i4, i8, p, or p.. A real array can consist of those same types, as well as structure, but it can be defined with multiple dimensions.
Here are some examples:
Pseudo array of alpha |
mparr, 10a5 |
One-dimensional real array of alpha |
mrarr, [10]a5 |
One-dimensional real array of structfields |
mrarr, [16]MyStruct |
Two-dimensional real array of decimal |
mrarr, [6,4]d8 |
Pseudo arrays are deprecated and we do not recommend their use.
In traditional Synergy, if a pseudo array is used in a class, it is converted to a real array by the compiler. A pseudo array is also converted to a real array if it is used outside a class and the code is compiled with -qcheck. When a pseudo array is converted to a real array, if -W4 is set, the compiler reports a level 4 warning regarding the change.
In Synergy .NET, pseudo arrays are treated as real arrays by the compiler. As a result, you must use the (*) syntax to pass a pseudo array as an argument.
You cannot declare a real array of .NET value types or objects or of CLS structures. For example, don’t declare [10]D_ADDR or [10]Int. (D_ADDR is shorthand for System.IntPtr and Int is shorthand for System.Int32 in .NET.) Instead, use either an array of i4 (or i8 for D_ADDR on 64-bit) or a dynamic array, such as [#]int or [#]D_ADDR.
To get the length of an entire static array, use ^SIZE on the array variable with empty brackets:
^size(mrarr[])
^SIZE without empty brackets returns the size of one element by default.
A dynamic array is an array whose size is determined at runtime. There are two dynamic array types: Array and ArrayList.
An Array is a one- or multi-dimensional array of objects or of any descriptor or value data type. You can declare a dynamic array whose upper bounds are determined at runtime by specifying a “#” for each array dimension. The declared variable will be type System.Array.
An ArrayList is a one-dimensional array of objects. You can declare an ArrayList as either a System.Collections.ArrayList or a Synergex.SynergyDE.Collections.ArrayList whose element type and size is determined at runtime. (See System-Supplied Classes for more information about these classes.)
Here are some examples:
One-dimensional Array of objects |
marr, [#]@MyClass |
One-dimensional Array of integers |
marr, [#]i4 |
Two-dimensional Array of structfields |
marr, [#,#]MyStruct |
One-dimensional ArrayList of objects |
marrl, @System.Collections.Array |
In addition, any descriptor type or value type may be specified as a boxed type in an Array. (See Boxing.) For example,
One-dimensional Array of boxed alphas |
marr, [#]@a |
The runtime upper bound for a System.Array is determined when you create the array using the NEW keyword. When an array of classes is created using the NEW keyword, each of the entries is initialized to ^NULL, unless initial values are specifically stated. For example,
marr = new MyClass[5] ;Create an array of 5 ^NULL object handles marr = new MyClass[5,7] ;Create an array of 5*7 ^NULL object handles marr = new i4[100] ;Create an array of 100 integers marr = new @a[20] ;Create an array of 20 boxed alpha handles
See NEW keyword for more information.
To specify initial values for a dimensioned NEW operation, enclose the initial values in curly braces. For example,
marr = new i4[#] {1,2,3} iarr = new int[#,#] {{1,2},{3,4}} ;2x2 array iarr = new int[#,#] {{1,2},{3,4},{5,6}} ;3x2 array iarr = new int[#,#] {{1,2,3,4},{5,6,7,8},{9,10,11,12}} ;3x4 array sarr3 = new string[#,#,#] {{{"A","B"}, {"C","D"}}, {{"E","F"}, {"G","H"}}, {{"I","J"}, {"K","L"}}} ;3x2x2 array
Unlike static arrays, dynamic arrays can be used as return types.
You can specify multiple arrayed items in a dynamic array by declaring a bracket pair for each array. For example,
marr, [#][#]@MyClass marr = new MyClass[3][#] marr[1] = new MyClass[2] marr[2] = new MyClass[2] marr[3] = new MyClass[2]
This declares an array of arrays, whose sizes are determined at runtime. For example, if the method meth1 was declared as
method meth1, void p1, [*]i4 ;Indicates it expects an array
and a local variable was declared as
myvar, [2]i4
then to pass myvar with its dimension information, you would use this syntax:
obj.meth1(myvar)
The ArrayList type is not truly an array but is actually a collections class that includes indexers for array-like access and methods for adding and removing elements. Unlike Array, the size of an ArrayList can also be dynamically changed at runtime. See System.Collections.ArrayList or Synergex.SynergyDE.Collections.ArrayList for more information.
Specifying a field position indicator
By default, Synergy DBL allocates data for a field beginning at the end of the current record, extending the record by the size of the field. Sometimes, however, you’ll want a field to begin at a specific point, or offset, in the record or group area. To begin a field at an offset, your field definition must include a position indicator (see position). A position indicator is always preceded by an “at” sign (@).
The data area referenced by the position indicator is the area that begins at the specified offset. If you specify an offset in an overlay record, the field must not extend beyond the end of the record being overlaid. If it does, you’ll get an “Cannot extend record with overlay field” compilation error. If the field extends beyond the data allocation for a nonoverlaid record, and does not overlay another field, Synergy DBL extends the record to include the newly defined field.
You can specify a position indicator for a field in a literal record only if the literal record is named or the field exists in a GROUP/ENDGROUP block.
You can specify a field’s starting position in one of two forms: absolute or relative. In the absolute form, a single decimal value represents the actual character position within the record or group at which the field begins. For example, a starting position of @2 means that the field begins at the second character position within the record or group.
In the relative form, the starting position is specified relative to the beginning of another record or field. The relative form has the following syntax:
name[+/–offset]
name
Either the name of the record or group currently being defined, the name of a previously defined record being overlaid, or the name of a previously defined field (in the current record or group, or in the record being overlaid).
offset
(optional) The number of characters from the beginning of name at which the field begins. Offset can be preceded by either a plus (+) or a minus (–), depending on where the field begins.
The following example shows the correct use of field position indicators:
record cusrec ;Master customer record name ,a30 address ,a50 company ,a30 phone ,a12 cdate ,d6 ;Next contact date day ,d2 @cdate month ,d2 @cdate+2 year ,d2 @cdate+4 record report_header ;Heading for report id ,a12 @1, "Inventory ID" onhand ,a10 @20, "Qty On-hand" commtd ,a9 @35, "Committed" order ,a8 @50, "On Order"
In the first record, the day field begins at the first character of cdate, the month field begins at the third character of cdate, and the year field begins at the fifth character of cdate. The layout for the second record creates blank fields between the defined fields.
Notice that if you add any fields to report_header, you must verify that the absolute field positions haven’t changed.
The following two examples are not valid. In the first example, the field specified in the field position indicator (key) has not yet been defined. In the second example, the specified record (rec1) is not the record that is being overlaid.
record client ;Invalid field overlays company ,a2 @key clnt_id ,a6 @key+2 key ,a8
and
record rec1 f1 ,a10 record rec2 f2 ,a10 record ,x f3 ,a3 @rec1+3
The example below breaks a field down into subfields:
record list name ,a25 phone ,a14, "(XXX) XXX-XXXX" area ,d3 @phone+1 prefix ,d3 @phone+6 pnum ,d4 @phone+10
The phone field name can reference the entire 14-character field. In addition, area references the second through fourth characters of phone, prefix references the seventh through ninth characters, and pnum references the last four characters.
The following example shows how easy it is to code report titles and line layouts directly from conventional report layout forms. You only need to worry about the beginning and ending positions of each report field; you don’t need to count spaces or define unused blank sections for each line.
record title ,a* @1, "Inventory ID" ,a* @20, "Qty On-hand" ,a* @35, "Committed" ,a* @50, "On Order" record line id ,a12 @1 onhand ,a10 @20 commtd ,a9 @35 order ,a8 @50
Initial values
Initial values are either literals or compile-time expressions. Additionally, in Synergy .NET, class fields may be initialized by a method call returning a value or a NEW keyword statement. (Also see Requiring an initial value below.) When you specify an initial value, Synergy DBL sets the field to that value before the first invocation of the routine, program, static class, or instance of a non-static class. For example,
Mystr, string, “abc” Myobj, @myclass, new myclass(10)
Initial values can be declared in field definitions that allocate data space, and in some cases they can be declared in DATA statements in the procedure division. Examples of field definitions that do not allocate data space are an overlay, a field declared using a field position indicator that overlays previously declared data, an external common, and a global data section that does not specify ,INIT.
In traditional Synergy, initial values are not allowed on stack records. (Remember that unspecified records may be stack records, depending on the compiler switches used.)
In traditional Synergy on Windows and Unix, local records behave mostly like static records, except fields are reset to their initial values if the routine is reclaimed or compiled with the -qrefresh option. If the routine is not reclaimed, the value of a field will be its value from the prior invocation. For more information on memory reclamation, see MAXMEM.
On Synergy .NET, stack records and DATA statement fields are always reset to their initial value.
Initial values on structures only apply to structfields and complex arrays of structfields, where the structfields’ initial values are those defined on the structure. If a structfield referencing a non-CLS structure declares an initial value, it supersedes any initial values declared on the structure.
When program execution begins, the default values of fields (and DATA statements in Synergy .NET) are as follows:
Data type |
Default value of fielda |
---|---|
Alpha |
blank |
Decimal, implied-decimal, integer, packed, or implied-packed |
0 |
Object |
^NULL |
.NET value type |
0 |
a. In traditional Synergy, fields in stack records and DATA statements, other than objects, have no default value. The SET UNINITIALIZED BREAK debugger command can help you check if you access stack record fields before they are initialized. See SET for more information.
You can override the default values by specifying initial values. Initial values cannot be longer than a single element of the field.
Initial values for alpha fields must be alpha expressions, while initial values for numeric fields must be numeric expressions. Initial values are loaded into a field element according to their rules for storing data into that field’s data type. See Assignment statements for information about storing data into different data types.
In traditional Synergy, initial values are not allowed on fields or properties that are string type. If you attempt to add one, an IVBAD error (“Initial value not allowed here”) will occur. String type initial values are permitted in .NET.
Initial values for arrays are assigned to each element successively, beginning with the first. You can provide initial values for one or more array elements as necessary; if you specify an initial value for one array element, you aren’t obligated to specify values for all elements. Synergy DBL assigns the default value to trailing array elements with no initial value.
If an initial value for an integer exceeds the maximum signed 64-bit value, the compiler generates an error. An error is also generated if an automatically size literal exceeds a 32-bit value. If the initial value does not exceed this maximum, the compiler automatically sizes it as an i4.
For information about initial values on DATA statements, see DATA.
In Synergy .NET (with .NET 7 and higher), you can apply the REQUIRED keyword to fields in a class to signify that an initial value for that field must be set. This enables you to create types that require class fields or properties to be initialized but still allow initialization using an object initializer.
You can set the initial values in one of two ways:
-
Call a constructor with the attribute {SetsRequiredMembers}
-
Use an object initializer when using the NEW operator to create an object
The REQUIRED keyword cannot be used for fields in a record or group, even when the record is in a class. Required members also cannot appear in structures or interfaces.
The following example declares a required field (field1) and two required properties (Prop1 and Prop2). Prop1 uses the simple property syntax, and Prop2 uses the more verbose declaration of both a get accessor and a set accessor. Two constructors are also defined in the class.
import System.Diagnostics.CodeAnalysis ;Needed for SetsRequiredMembers namespace ns public class MyClass public required field1, int public required readwrite property Prop1, @string public required property Prop2, int method get proc mreturn 0 endmethod method set proc nop endmethod endproperty {SetsRequiredMembers} public method MyClass ; constructor proc field1 = 1 Prop1 = "Bob" Prop2 = 2 endmethod public method MyClass ;Other constructor parm, int proc endmethod endclass endnamespace
We can instantiate MyClass in one of two ways:
-
Use any constructor that has {SetsRequiredMembers} applied to it. This means we can use the MyClass() constructor taking no arguments because it has the SetsRequiredMembers attribute. We cannot use the other constructor, MyClass(int), taking an int parameter because it doesn’t have that attribute.
data cvar = new MyClass() ;OK data cvar2 = new MyClass(5) ;Reports an error (E_INITVERR)
-
Use any constructor regardless of the presence of {SetsRequiredMembers}, but ensure the required fields and properties are manually set in an object initializer that follows the constructor call.
; No error because of the object initializer: data cvar3 = new MyClass(5) { field1 = 100, Prop1 = "Fred", Prop2 = 350 }
All members marked REQUIRED must be set.
For constructors marked with {SetsRequiredMembers}, the compiler does not check the body of the constructor to ensure that the necessary required fields and properties were assigned. Instead, the developer must set those class members, and the compiler trusts that the constructor was implemented correctly. The only role of {SetsRequiredMembers} is to suppress all E_INITVERR errors that would normally be produced from calling a constructor where the object initializer didn’t set all the required members.
Static fields
The STATIC modifier can only be specified on a field that is not defined in a common, literal, structure, or nonstatic record. If the owning record is static, then STATIC is the default field_mod.
Sample field definitions
Here are some sample field definitions:
record ,a3, "XYZ" inval ,a*, "This is the initial value" fld1 ,2d7, 123,987 fld2 ,[2,2,2]d3.1, 11.1, 11.2, 12.1, 12.2, & 21.1, 21.2, 22.1, 22.2 date ,d6, 32792 month ,d2 @date day ,d2 @date+2 year ,d2 @date+4
The following example further illustrates the three ways to access arrays. First, let’s assume the following data division statements:
record array1 ,4a6, "ABCDEF", "GHIJKL", & "MNOPQR", "STUVWX" array2 ,3d3, 123, 4, 789 .align LONG array3 ,[3,2]d2, 11, 12, & 21, 22, & 31, 32
The following data space is allocated from left to right and from top to bottom (shaded boxes are not part of the data space):
Variable |
Data area (characters) |
|||||
---|---|---|---|---|---|---|
array1(1) |
A |
B |
C |
D |
E |
F |
array1(2) |
G |
H |
I |
J |
K |
L |
array1(3) |
M |
N |
O |
P |
Q |
R |
array1(4) |
S |
T |
U |
V |
W |
X |
array2(1) |
1 |
2 |
3 |
|
|
|
array2(2) |
0 |
0 |
4 |
|
|
|
array2(3) |
7 |
8 |
9 |
|
|
|
alignment filler |
|
|
|
|
|
|
array3[1,1] |
1 |
1 |
|
|
|
|
array3[1,2] |
1 |
2 |
|
|
|
|
array3[2,1] |
2 |
1 |
|
|
|
|
array3[2,2] |
2 |
2 |
|
|
|
|
array3[3,1] |
3 |
1 |
|
|
|
|
array3[3,2] |
3 |
2 |
|
|
|
|
Here are some examples of variable references and the data they obtain:
Variable reference |
Data obtained |
---|---|
array1(1) |
ABCDEF |
array1(3) |
MNOPQR |
array2 |
123 |
array2(7,7) |
7 |
array1(array2(6:1),12) |
DEFGHIJKL |
array3[2,2] |
22 |
array3[2,2] (3:4) |
3132 |