Creating and Defining Structures


A named structure is created by executing a structure-definition expression, which is an expression of the following form:

{Structure_Name, Tag_Name1 : Tag_Definition1, ..., Tag_Namen : Tag_Definitionn}

Anonymous structures are created in the same way, but with the structure’s name omitted.

{Tag_Name1 : Tag_Definition1 , ..., Tag_Namen : Tag_Definitionn}

Anonymous structures can also be created and combined using the CREATE_STRUCT function.

Tag names may not be IDL Reserved Words, and must be unique within a given structure, although the same tag name can be used in more than one structure. Structure names and tag names follow the rules of IDL identifiers: they must begin with a letter; following characters can be letters, digits, or the underscore or dollar sign characters; and case is ignored.

Tip: By default structure tag names are all uppercase, regardless of how they are written in the IDL code. However, you can create tag names that are mixed case by using the PRESERVE_CASE keyword to CREATE_STRUCT. Tag names with mixed case are still case insensitive - you can dereference them using any case. The case is only used when displaying the output from HELP or when the TAG_NAMES function is called.

As mentioned above, each tag definition is a constant, variable, or expression whose structure defines the structure and initial value of the field. The result of the structure definition expression is an instance of the structure, with each field set equal to its tag definition.

A named structure that has already been defined can be referred to by enclosing the structure’s name in braces, as shown below:

{Structure_Name }

The result of this expression is a structure of the designated name.

Note: When a new instance of a structure is created from an existing named structure, all of the fields in the newly-created structure are zeroed. This means that fields containing numeric values will contain zeros, fields containing string values will contain empty strings, and fields containing pointers or objects will contain null pointers or null objects. In other words, no matter what data the original structure contained, the new structure will contain only a template for that type of data.

Also, when making a named structure that has already been defined, the tag names need not be present:

{Structure_Name, expression1, ..., expressionn}

All of the expressions must agree in structure with the original tag definition.

Once defined, a given named structure type cannot be changed. If a structure definition with tag names is executed and the structure already exists, each tag name and the structure of each tag field must agree with the original definition. Anonymous structures do not have this restriction because each instance has its own definition.

Structure Inheritance


Structures can inherit tag names and definitions from other structures. To cause one structure to inherit tags from another, use the INHERITS specifier. For example, if we define a structure one as follows:

A = {one, data1a:0, data1b:0L }

we can define a second structure two that includes the tags from the one structure with the following definition statement:

B = { two, INHERITS one, data2:0.0 }

This is the same as defining the structure two with the statement:

B = { two, data1a:0, data1b:0L, data2:0.0 }

Note that the fields of the one structure are included in the two structure in the position that the INHERITS specifier appears in the structure definition.

Remember that tag names must be unique. If you use structure inheritance, be sure that the tag names in the inherited structure do not conflict with the tag names in the inheriting structure.

Structures that are inherited must be defined before the inheriting structure can be defined. If a structure inherits tags from another structure that is not yet defined, IDL will search for a routine to define the inherited structure. If the inherited structure cannot be defined, definition of the new structure fails.

While structure inheritance can be used with any structure, it is most useful when working with object class structures. When the INHERITS specifier is used in a class structure definition, it has the added effect of defining the inheriting object as a subclass of the inherited class.

Example of Creating a Structure


Assume that a star catalog is to be processed. Each entry for a star contains the following information: star name, right ascension, declination, and an intensity measured each month over the last 12 months. A structure for this information is defined with the following IDL statement:

A = {star, name:'', ra:0.0, dec:0.0, inten:FLTARR(12)}

This structure definition is the basis for all examples in this section. The statement above defines a structure type named star, which contains four fields. The tag names are name, ra, dec, and inten. The first field, with the tag name, contains a scalar string as given by its tag definition. The following two fields each contain floating-point scalars. The fourth field, inten, contains a 12-element, floating-point array. Note that the type of the constants, 0.0, is floating point. If the constants had been written as 0, the fields ra and dec would contain short integers.

The same structure is created as an anonymous structure by the statement:

A = {name:'', ra:0.0, dec:0.0, inten:FLTARR(12)}

or by using the CREATE_STRUCT function:

A = CREATE_STRUCT('name', '', 'ra', 0.0, 'dec', 0.0, $
    'inten', FLTARR(12))

Arrays of Structures


An array of structures is simply an array in which each element is a structure of the same type. The referencing and subscripting of these arrays (also called structure arrays) follow the same rules as simple arrays.

Creating an Array of Structures


The easiest way to create an array of structures is to use the REPLICATE function. The first parameter to REPLICATE is a reference to the structure of each element. Using the example in Examples of Structure References and assuming the STAR structure has been defined, an array containing 100 elements of the structure is created with the following statement:

cat = REPLICATE({star}, 100)

Alternatively, since the variable A contains an instance of the structure STAR, then

cat = REPLICATE(A, 100)

Or, to define the structure and an array of the structure in one step, use the following statement:

cat = REPLICATE({star, name:'', ra:0.0, dec:0.0, $
    inten:FLTARR(12)}, 100)

The concepts and combinations of subscripts, subscript arrays, subscript ranges, fields, nested structures, etc., are quite general and lead to many possibilities, only a small number of which can be explained here. In general, any structures that are similar to the examples above are allowed.

Examples of Arrays of Structures


This example uses the above definition in which the variable CAT contains a star catalog of STAR structures.

;Set the name field of all 100 elements to "EMPTY."
cat.name = 'EMPTY'
 
;Set the i-th element of cat to the contents of the star structure.
cat[I] = {star, 'BETELGEUSE', 12.4, 54.2, FLTARR(12)}
 
;Store 0.0 into cat[0].ra, 1.0 into cat[1].ra, ..., 99.0 into 
;cat[99].ra
cat.ra = INDGEN(100)
 
;Prints name field of all 100 elements of cat, separated by commas 
;(the last field has a trailing comma).
PRINT, cat.name + ','
 
;Find index of star with name of SIRIUS.
I = WHERE(cat.name EQ 'SIRIUS')
 
;Extract intensity field from each entry. Q will be a 12 by 100 
;floating-point array.
Q = cat.inten
 
;Plot intensity of sixth star in array cat.
PLOT, cat[5].inten
 
;Make a contour plot of the (7,46) floating-point array ;taken from 
;months (2:8) and stars (5:50).
CONTOUR, cat[5:50].inten[2:8]
 
;Sort the array into ascending order by names. Store the result 
;back into cat.
cat = cat(SORT(cat.name))
 
;Determine the monthly total intensity of all stars in array. 
;monthly is now a 12-element array.
monthly = cat.inten # REPLICATE(1,100)

Structure References


The basic syntax of a reference to a field within a structure is as follows:

Variable_Name.Tag_Name

Variable_Name must be a variable that contains a structure. Tag_Name is the name of the field and must exist in the structure. If the field referred to by the tag name is itself a structure, the Tag_Name can optionally be followed by one or more additional tag names, as shown by the following example:

var.tag1.tag2

Each tag name, except possibly the last, must refer to a field that contains a structure.

Number of Structure Tags


The function N_TAGS(Structure) returns the number of fields in a structure. To obtain the size, in bytes, of a structure call N_TAGS with the /LENGTH keyword.

Names of Structure Tags


The function TAG_NAMES(Structure) returns a string array containing the names of each tag. To return the name of the structure itself, call TAG_NAMES with the /STRUCTURE_NAME keyword.

Access Fields by Tag Number


A tag can be referenced using its index rather than its tag name. The tag index should be enclosed in parentheses, as follows:

Variable_Name.(Tag_Index)... ... ...

The Tag_Index ranges from zero to the number of fields minus one.

Note: The Tag_Index is an expression, the result of which is taken to be a tag position. In order for the IDL parser to understand that this is the case, you must enclose the Tag_Index in parentheses. This is not an array indexing operation, so using square brackets [ ] is not allowed in this context.

Subscripted Structure References


A subscript specification can be appended to the variable or tag names if the variable is an array of structures or if the field referred to by the tag contains an array. Scalar fields within a structure can also be subscripted, provided the subscript is zero.

Variable_Name.Tag_Name[Subscripts]

Variable_Name[Subscripts].Tag_Name...

Variable_Name[Subscripts].Tag_Name[Subscripts]

Each subscript is applied to the variable or tag name it immediately follows. The syntax and meaning of the subscript specification is similar to simple array subscripting in that it can contain a simple subscript, an array of subscripts, or a subscript range. If a variable or field containing an array is referenced without a subscript specification, all elements of the item are affected. Similarly, when a variable that contains an array of structures is referenced without a subscript but with a tag name, the designated field in all array elements is affected. The complete syntax of references to structures follows. (Optional items are enclosed in braces, {}.)

Structure_reference:= Variable_Name{[Subscripts]}.Tags

Tags:= {Tags.}Tag

Tag:= Tag_Name{[Subscripts]}

For example, all of the following are valid structure references:

A.B
A.B[N, M]
A[12].B
A[3:5].B[*, N]
A[12].B.C[X, *]

Storing Into Array Fields


The semantics of storing into structure array fields is slightly different than storing into simple arrays. The main difference is that with structures, a subscript range must be used when storing an array into part of an array field. With normal arrays, when storing an array inside part of another array, use the subscript of the lower-left corner, not a range specification. Other differences occur because the size and type of a field are fixed by the original structure definition, and the normal semantics of dynamic binding are not applicable. The rules for storing into array fields are as follows:

VAR.ARRAY_TAG = Scalar_Expression

All elements of VAR.tag are set to Scalar_Expression. For example:

;Set all 12 elements of A.inten to 100.
A.inten = 100

VAR.TAG = Array_Expression

Each element of Array_Expression is copied into the array VAR.tag. If Array_Expression contains more elements than the destination array does, an error results. If it contains fewer elements than VAR.TAG, the unmatched elements remain unchanged. For example:

;Set A.inten to the 12 numbers 0, 1, 2,..., 11.
A.inten = FINDGEN(12)
 
;Set A.inten[0] to 1 and A.inten[1] to 2. The other elements 
;remain unchanged.
A.inten = [1, 2]

VAR.TAG[Subscript] = Scalar_Expression

The value of the scalar expression is copied into the designated element of the destination. If Subscript is an array of subscripts, the scalar expression is copied into the designated elements. For example:

;Set the sixth element of A.inten to 100. 
A.inten[5] = 100
 
;Set elements 2, 4, and 6 to 100.
A.inten[[2, 4, 6]] = 100

VAR.TAG[Subscript] = Array_Expression

Unless VAR.tag is an array of structures, the subscript must be an array. Each element of Array_Expression is copied into the element given by the corresponding element subscript. For example:

;Set elements 2, 4, and 6 to the values 5, 7, and 9 respectively.
A.inten[[2, 4, 6]] = [5, 7, 9]

VAR.TAG[Subscript_Range] = Scalar_Expression

The value of the scalar expression is stored into each element specified by the subscript range. For example:

;Sets elements 8, 9, 10, and 11 to the value 5.
A.inten[8:*] = 5

VAR.TAG[Subscript_Range] = Array_Expression

Each element of the array expression is stored into the element designated by the subscript range. The number of elements in the array expression must agree with the size of the subscript range. For example:

;Sets elements 3, 4, 5, and 6 to the numbers 0, 1, 2, and 3, ;respectively.
A.inten[3:6] = FINDGEN(4)

Examples of Structure References


The name of the star contained in A is referenced as A.NAME. The entire intensity array is referred to as A.INTEN, while the n-th element of A.INTEN is A.INTEN[N]. The following are valid IDL statements using the STAR structure:

;Store a structure of type STAR into variable A. Define the values 
;of all fields.
A = {star, name:'SIRIUS', ra:30., dec:40., inten:INDGEN(12)}
 
;Set name field. Other fields remain unchanged.
A.name = 'BETELGEUSE'
 
;Print name, right ascension, and declination.
PRINT, A.name, A.ra, A.dec
 
;Set Q to the value of the sixth element of A.inten. Q will be a 
;floating-point scalar.
Q = A.inten[5]
 
;Set ra field to 23.21.
A.ra = 23.21
 
;Zero all 12 elements of intensity field. Because the type and size 
;of A.inten are fixed by the structure definition, the semantics of 
;assignment statements is different than with normal variables.
A.inten = 0
 
;Store fourth thru seventh elements of inten field in variable B.
B = A.inten[3:6]
 
;The integer 12 is converted to string and stored in the name field 
;because the field is defined as a string.
A.name = 12
 
;Copy A to B. The entire structure is copied and B contains a STAR 
;structure.
B = A

Using HELP with Structures


Use the HELP,/STRUCTURE command to determine the type, structure, and tag name of each field in a structure. In the example above, a structure was stored into variable A. The statement,

HELP, /STRUCTURE, A

prints the following information:

** Structure STAR, 4 tags, length=40:
NAME            STRING    'SIRIUS'
RA              FLOAT           30.0000
DEC             FLOAT           40.0000
INTEN           INT       Array(12)

Using HELP with anonymous structures prints the structure’s name as a unique number enclosed in angle brackets. Calling HELP with the STRUCTURE keyword and no parameters prints a list of all defined, named structure types and their tag names.

Relaxed Structure Assignment


The IDL “=” operator is unable to assign a structure value to a structure with a different definition. For example, suppose we have an existing structure definition SRC, as follows:

source = { SRC, A:FINDGEN(4), B:12 }

and we wish to create a second instance of the same structure, but with slightly different data and a different field:

dest = { SRC, A:INDGEN(2), C:20 }

Attempting to execute these two statements at the IDL command prompt gives the following results:

% Conflicting data structures: <INT       Array[2]>,SRC.
% Execution halted at:  $MAIN$

Versions of IDL beginning with IDL 5.1 include a mechanism to solve this problem. The STRUCT_ASSIGN procedure performs “relaxed structure assignment,” which is a field-by-field copy of a structure to another structure. Fields are copied according to the following rules:

  1. Any fields found in the destination structure that are not found in the source structure are “zeroed” (set to zero, the empty string, or a null pointer or object reference depending on the type of field).
  2. Any fields in the source structure that are not found in the destination structure are quietly ignored.
  3. Any fields that are found in both the source and destination structures are copied one at a time. If necessary, type conversion is done to make their types agree. If a field in the source structure has fewer data elements than the corresponding field in the destination structure, then the “extra” elements in the field in the destination structure are zeroed. If a field in the source structure has more elements than the corresponding field in the destination structure, the extra elements are quietly ignored.

Using STRUCT_ASSIGN, we can make the assignment that failed using the = operator:

source = { src, a:FINDGEN(4), b:12 }
dest = { dest, a:INDGEN(2), c:20 }
STRUCT_ASSIGN, source, dest, /VERBOSE

IDL prints:

% STRUCT_ASSIGN: SRC tag A is longer than destination. 
                 The end will be clipped.
% STRUCT_ASSIGN: Destination lacks SRC tag B. Not copied.

If we check the variable dest, we see that it has the definition of the dest structure and the data from the source structure:

HELP, dest, /STRUCTURE

IDL prints:

** Structure DEST, 2 tags, length=6:
   A               INT       Array[2]
   C               INT              0

Using Relaxed Structure Assignment


Why would you want to use Relaxed Structure Assignment? One case where this type of structure definition is very useful is in restoring object structures into an environment where the structure definition may have changed since the restored objects were saved.

Suppose you have created an application that saves data in structures. Your application may use the IDL SAVE routine to save the data structures to disk files. If you later change your application such that the definition of the data structures changes, you would not be able to restore your saved data into your application’s framework without relaxed structure assignment. The RELAXED_STRUCTURE_ASSIGNMENT keyword to the RESTORE procedure allows you to make relaxed assignments in such cases.

To see how this works, try the following exercise:

  1. Start IDL, create a named structure, and use the SAVE procedure to save it to a file:

    mystruct = { STR, A:10, B:20L, C:'a string' }
    SAVE, mystruct, FILE='test.dat'
  2. Exit and restart IDL.
  3. Create a new structure definition with the same name you used previously:

    newstruct = { STR, A:20L, B:10.0, C:'a string', D:ptr_new() }
  4. Attempt to restore the variable mystruct from the test.dat file:

    RESTORE, 'test.dat'
    IDL prints:
    % Wrong number of tags defined for structure: STR.
    % RESTORE: Structure not restored due to conflict with
               existing definition: STR.
  5. Now use relaxed structure definition when restoring:

    RESTORE, 'test.dat', /RELAXED_STRUCTURE_ASSIGNMENT
  6. Check the contents of mystruct:

    HELP, mystruct, /STRUCTURE
    IDL prints:
    ** Structure STR, 4 tags, length=20:
       A               LONG                10
       B               FLOAT           20.0000
       C               STRING    'a string'
       D               POINTER   <NullPointer>

The structure in the variable mystruct now uses the definition from the new version of the STR structure, but contains the data from the old (restored) structure. In cases where the data type of a field has changed, the data type of the old data element has been converted to the new data type. Fields in the new structure definition that do not correspond to fields in the old definition contain “zero” values (zeroes for numeric fields, empty strings for string fields, null pointer or references for pointer or reference fields).

Trailing Commas in Structure Creation


When creating structures, a trailing comma at the end of a sequence of tag name/value pairs is ignored. For example:

a = {field1: 1, field2: "2", field3: 3.0, }
help, a

IDL prints:

** Structure <e9757020>, 3 tags, length=32, data length=22, refs=1:
  FIELD1          INT              1
  FIELD2          STRING    '2'
  FIELD3          FLOAT           3.00000

This feature makes it simpler to modify existing structures in code by just adding in another row without worrying about a missing comma. For example, if you defined a structure:

a = { $
  field1: 1, $
  field2: "2", $
  field3: 3.0, $
}

You can easily add a fourth row:

a = { $
  field1: 1, $
  field2: "2", $
  field3: 3.0, $
  field4: "four", $ ; new row, did not have to touch the third row
}

Having a trailing comma (or not) is completely optional and makes no difference to the created structures or its contained fields.

Version History


9.2

Added support for trailing commas.