Modified (Example 3) August 2015
Arrays can be passed as parameters to functions/procedures/subroutines, but the techniques vary depending on whether the array is static or dynamically mapped. For static mapped arrays, the only available technique is to map an X variable as an overlay over the array, e.g.
map1 blob
map2 array(200)
map3 field1,s,4
map3 field2,f,6
...
call Proc(blob) ! pass the array by means of the overlay
Passing DIMX arrays, by contrast, requires one of the following special methods:
• Using Dynamic Overlays to pass a copy of the array as a blob.
• Using pass-by-reference; see following discussion
Unlike the normal pass-by-value method for parameter passing, in which the called routine receives a copy of the parameter, pass-by-reference provides the called routine an alias—essentially a pointer—to the original parameter/array. This is much more efficient than making a copy of the entire array and then having to transfer that copy back to the original on return. Both the alias array and the original array refer to the same set of elements; the only difference between them is the name. Any changes made to the alias array (including resizing or auto-extending it) are directly made to the original array (since they are the same array).
Passing an array by-reference is architecturally nearly the same as just accessing it globally—i.e., using ++extern or ++pragma auto_extern within the called routine to bring the global array into scope. The main advantages are that it makes the code (on the calling side) somewhat more explicit/self-documenting, and it provides the called routine the flexibility to act on different source arrays (determined by the caller).
The syntax for indicating an array to be passed or received by reference is to use an empty set of parentheses, e.g. ARY(), as in the following examples.
Example 1: Pass DIMX array of structures to function / procedure
dimx CUST(0), ST_CUST, auto_extend
...
call Add'Items'To'Array( CUST(), 5 ) ! pass CUST() by reference to function
...
procedure Add'Items'To'Array( ary() as ST_CUST, count as b2) ! rcv array by reference
map1 p,i,2
map1 i,i,2
p = .extent( ary() )
for i = p+1 to p+count
ary(i).field = <data> ! ary() is effectively alias for passed CUST()
next i
endprocedure
In the above example, the array ary() within the procedure becomes an alias to the array CUST() in the calling routine, and any changes made to ary() within the procedure are reflected in CUST() immediately.
Neither the compiler nor runtime system (currently) can verify that the passed and received structure types are the same; the runtime system can only verify that the structure sizes are the same. This could allow for a kind of polymorphism—or more likely, subtle bugs—if you do not take care to make sure that the structure types are in fact the same. In the above example, both the source array CUST() and the received array ary() are explicitly declared as the same structure type, ST_CUST.
Note: Do not attempt to use XPUTARG to copy the array back to the caller; this would make no sense.
Example 2: Pass DIMX array of dynamic blobs to function / procedure
The syntax and other logic for passing a DIMX array of X,0 elements is essentially the same as in the example above, e.g.
dimx XRY(0),X,0,auto_extend ! declare DIMX array of variable length blobs
...
call Add'Items'To'Array( XRY() ) ! pass XRY() by reference to procedure
...
procedure Add'Items'To'Array( xry() as x0) ! rcv array by reference
map1 i,f
do while <some condition>
xry(i) = <some variable length string or binary data>
loop
endprocedure
The source array must be defined at the global or module-scope—i.e., PRIVATE DIMX—level, rather than locally within a function. This is to ensure that the contents remain valid when referenced within another scope.
Currently this only works for X,0 (and not S,0) variables. The restriction may be lifted, but there is no particular need since dynamic X variables are practically interchangeable with dynamic S variables anyway. The main distinction between X and S has to do with the whether bytes past the first null are considered significant, but this isn't really a factor when loading string data into dynamic X variables, since the variable size would be trimmed to eliminate any bytes after the null terminator anyway.
Example 3: Pass DIMX array of structures to XCALL SBX
Passing a DIMX array of structures to a subroutine (SBX) is similar to passing one to a function or procedure, at least from the calling side, e.g.
dimx myary(0),ST_MYSTRUCT,auto_extend ! define source array
...
xcall MYSBX,myary(),... ! specify in arg list with empty ()
On the receiving side (within the SBX), because there is no equivalent of the formal parameter declaration like there is in Functions and Procedures, the SBX has to declare a surrogate array using a special form of the DIMX statement with the byref modifier, and the initial subscript value set to -1, e.g.: .:
dimx locary(-1), ST_MYSTRUCT, byref ! define surrogate local array
The surrogate local array will not work correctly unless the subscript in the dimx statement is -1, and the byref clause is specified. Prior to 6.1.1415 (compiler edit 744), the compiler failed to complain about invalid subscript values such as 0 or 1.
This method only works for passing a Dynamic array of structures, not for a simple dynamic array of standard types.
To receive the array passed by reference, use the normal xgetargs syntax, but with an empty set of parentheses for the local surrogate array, e.g.
xgetargs locary(), ...
Note that as with the function / procedure examples, there is no need for any xputarg statement to send the updated array back to the caller, since the called routine effectively shares the same copy of the array with the caller.
History
2014 November, A-Shell 6.1.1398: Pass-by-reference extended to XCALL (SBX)
2014 October, A-Shell 6.1.1391: Pass-by-reference feature introduced for functions and procedures (not XCALLs)