A structure is, as the name suggests, a layout of standard SOUL variables.
The layout of a structure is described by a Structure block, and variables with that layout are indicated by the Structure datatype on variable declarations.
Structure blocks and variable declaration
A Structure block has the following syntax:
Structure structureName [requiredSuffix=suffix] variable declarations ... End Structure [structureName]
- structureName can consist of any valid alphanumeric and other non-separator characters.
- suffix is a set of characters that must end the name of any variable being declared with the structure.
- The variables in a structure are sometimes referred to as the "structure members."
Note: You can also define "local" structures that apply only in the scope in which they are defined, which may be within a class or a method or another level of the program.
The variable declarations consist of one or more declarations of exactly the same format as a SOUL %variable declaration, with the exception that the variable names must not begin with a percent sign:
structure budgie name is string len 6 color is string len 32 age is fixed dp 2 end structure
Like a standard SOUL %variable declaration, the keyword Is is optional. Valid datatypes include String, Float, Fixed, Longstring, and (as described later) Structure, Object, Collection, and Enumeration. A Structure member can also be an Array, but it cannot be Static or Global. Structure members may contain an Initial clause that indicates the initial value for a member in an instance of the structure.
A Structure block does not actually define a structure variable, but instead it defines the layout that such a variable would have. To define a variable with the layout specified on the structure statement, simply specify a Structure variable:
%var [is] Structure structureName
The individual members of the structure variable can then be referenced in much the same way that image items are referenced, with the exception that a structure does not have to be Prepared:
structure budgie color is longstring size is float end structure %sunshine is structure budgie %sunshine:color = 'blue' %sunshine:size = 18
Structures also differ significantly from Images in that multiple variables can be declared with the same structure and that structure variables can be assigned to each other as a whole, rather than a member at a time:
%sunshine is structure budgie %tweetie is structure budgie %sunshine:color = 'blue' %sunshine:size = 18 %tweetie = %sunshine
Note: Structure assignment is assignment by value: the elements of the source structure are copied one by one (very efficiently) to the target structure, so that subsequent changes to the source Structure variable do not affect the target.
For example, the following code prints
%sunshine is structure budgie %tweetie is structure budgie %sunshine:color = 'blue' %tweetie = %sunshine %sunshine:color = 'yellow' print %tweetie:color
The behavior of object variables contrasts with this: assignment is by reference, as described later.
Working with structure variables
Structure variables can be passed as both input and output subroutine parameters:
%sunshine is structure budgie %sunshine:color = 'blue' call feed(%sunshine) ... subroutine feed(%birdie is structure budgie)
Structure variables can be compared, though only for equality (order comparisons such as GE, LE, GT, and LT are not supported):
%sunshine is structure budgie %tweetie is structure budgie ... if %sunshine ne %tweetie then print 'More millet, please' end if
Structures can be embedded inside of other structures:
structure bird color is longstring size is float end structure structure budgie name is string len 6 stuff is structure bird age is fixed dp 2 end structure
In this case, members of inner structures are accessed by appending their names to the outer structure members:
%flapper is structure budgie ... %flapper:stuff:color = 'green'
A structure variable can be an Array, and structures can contain Arrays:
structure auction item is string len 32 nbids is fixed bid is fixed dp 2 array(20) end structure ... %onBlock is structure auction %lot is structure auction array(50)
In this case, the subscripts must come after the variable name, the member name, or both, where appropriate:
%onBlock:bid(%i) = %input ... %lot(%j):item = 'Tiffany lamp' ... %lot(%x):bid(%y) = %bid
Structure variables reside in VTBL in space allocated at compile-time so, as stated earlier, have no need to be "prepared." However, at times it may be useful to "prepare" a structure variable, that is, set it to its initial default values. Because there is no "Prepare Structure" statement, this is best accomplished either by setting the individual members or (more efficiently) by keeping a template or initial structure variable to assign to a structure variable which is to be logically "prepared":
structure budgie name is string len 6 color is string len 8 initial('blue') end structure %budgieTemplate is structure budgie %sunshine is structure budgie ... * Re-initialize %sunshine %sunshine = %budgieTemplate
The RequiredSuffix parameter
The RequiredSuffix parameter on structure definitions, like the corresponding parameter on class definitions, provides a way of enforcing naming standards for structure variable names. For example, if you have a structure definition like this:
structure budgie requiredSuffix=.parakeet name is string len 6 color is string len 8 initial('blue') end structure
All Structure variables of type
Budgie would have to end in the characters
.parakeet, making it relatively east to detect references to this type of variable in SOUL code:
%my.parakeet:color = 'blue' ... %current.parakeet = %my.parakeet
Comparing structures and classes
Structures and structure variables provide a rich new facility to SOUL programmers. Yet, oddly, the recommendation is to use them sparingly, and in general, to use Classes and Object variables instead. There are many reasons for this:
- Assignment of object variables is much more efficient because only the reference (pointer) is actually copied, not all the values.
- Object variables don't necessarily take up VTBL space and, in fact, are swapped out to CCATEMP as needed.
- Object variables prevent multiple references to the same object.
While the first two points are important, the last is most critical to an applications programmer. To illustrate why this is so, consider a
structure customer name is string len 64 balance is fixed dp 2 end structure
Suppose two variables have this structure, and you assign one to the other:
%currentCust is structure customer %biggestCust is structure customer ... %currentCust = %biggestCust ... %currentCust:balance = %currentCust:balance - %fee
Now, presumably, you must fix the
balance in %biggestCust or hope that %biggestCust is not referenced again. As more and more copies of the same customer's data are propagated around the code, the task of keeping them in sync become increasingly complicated. By using an object instead to represent the customer, this problem goes away, because all the object variables that refer to the same customer would point to the same underlying object (assuming the application is properly designed). Any changes to the underlying object would be reflected immediately in all the references to that object.
Some basic guidelines for when a Structure is to be used instead of an object are:
- Any data structure that represents a real entity or object — a noun, if you will — should be an object, not a structure. Structures should generally be used for measurements or attributes of objects.
- Despite the richness of the Structure facility, structures should be small, that is, with no more than five members and usually only two or three. If they get much bigger than this, it is likely that an object is a more appropriate model for the data being represented.
- Any data structure that represents an attribute of an object or method — an adjective, if you will — might make sense as a Structure.
- Because structures are statically allocated in VTBL, accessing data in a Structure can be somewhat more efficient than accessing data in an Object (and in an Image, incidentally), so might be useful in cases where performance of individual member access is the paramount concern.