Rewritten July 2022
The process of passing parameters in to a function or procedure actually has two separate parts: matching and passing.
Matching
At compile time, the compiler has to match the calling parameters to the receiving parameters, using one or both of the following:
• | Match by order: If the caller simply lists the parameters in the calling statement without naming them, then they are matched to the receiving parameter by order, i.e. the first parameter in the function call is matched to the first parameter in the function definition, etc. |
• | Match by name: If the calling statement identifies the parameters by name, then they are matched to the receiving parameters by name. |
Example
call fn'produce("apple", 0.97, 4.5, "extra")
call fn'produce(itemname$="peach", lbs=9)
call fn'produce("surprise")
...
function fn'produce(itemname$ as s20:inputonly, price=0 as f6:inputonly, lbs as f6:inputonly) as f6
if price = 0 then
price = fn'price(itemname$)
endif
...
In the above example, the first call specifies four parameters without any names, so they will have to match to the receiving parmeters by order, i.e. itemname$ receives "apple", price receives 0.97, lbs receives 4.5. The fourth parameter, extra, is passed to the function but not automatically matched to any receiving parameters; the function could detect it using .ARGCNT and retrieve it manually into a variable using XGETARG.
The second call specifies names, so the second parameter (lbs=9) is properly matched to the third parameter on the receiving end. Nothing is passed to the price parameter, which is only allowed since the function definition specifies a default value (price=0).
The third call also lacks names, and only specifies one parameter, which will be matched to itemname$. The other two parameters will default on the receiving end, either based on an explicitly set default (price=0), or else to 0 or "" depending on the receiving data type.
Note that although it is possible for the caller to specify some parameters by order and some by name, the named parameters must all follow the ordered ones. Skipping over parameters is only allowed when the parameter has a specified default value.
Passing
At run-time, the values of the parameters specified in the calling statement are passed to the receiving parameters by one of the following methods:
• | Pass by Value: This applies to most situations, and involves making a separate copy of the source value into the received parameter. If the parameter types are not the same, conversions will be performed just like they would in an assignment statement. Changes to the received variable do not affect the calling variable unless XPUTARG is used to copy the value back. |
• | Pass by Reference: This applies only to the passing of entire arrays or collections, and consists of linking the receiving parameter to the calling parameter so that they share the same memory. Any changes update both the the calling and received parameters simultaneously, and XPUTARG is not used. |
Detecting Optional Parameters
As noted above in the description of parameter matching, trailing parameters are always optional. Beyond that, in order to make a parameter optional, it needs to have a default value in the function definition. In the fn'produce() function, the first parameter itemname$ is mandatory except when the caller specifies no parameters at all. The price parameter is optional because of the default value, and lbs is optional because it's a trailing parameter.
The following methods can be used within the function to determine whether an optional parameter was actually passed:
The .ARGCNT dot variable indicates the total number of parameters. Since this will include any non-trailing optional parameters, it is only good for identifying the number of trailing parameters that were not specified by the caller—or conversely, the number of additional parameters that were specified beyond those defined int he function declaration.
Any non-trailing optional parameters that were not passed by the caller will be set to the default value. So, depending on the default value set, you may be able to determine whether the parameter was passed.
For arrays and collections passed by reference (see the related topics listed below), you can set the default value to .NULL to make them optional, and then you can test if they were passed by checking the .EXTENT of the received array or collection. -1 indicates nothing was passed, while zero or more indicates the extent of the actual array of collection passed.
See Also
• | Sample programs in EXLIB:[908,61] demonstrate array passing by reference. |
History
2018 May, A-Shell 6.5.1635: When passing expressions to/from functions and SBX subroutines, if the target is type X, use a physical rather than logical transfer. This matches the behavior of assignments, and closes a loophole that previously occurred when the source expression was of the form VAR[x;y] or VAR[x,y] where VAR was of type X. Previously the transfer was being terminated at the first null even when the target variable was of type X.
Note that this change affects all variations of XGETARG and XPUTARG, as well as implicit parameter passing to functions/procedures, potentially changing the runtime behavior of existing compiled programs, but only in cases where a string or non-numeric expression is passed to an X type variable. Given the narrow range of situations affected, and our belief that the new behavior is more in line with how programmers thought that it had been working all along, not to mention the fact that it is now consistent with the assignment statement logic, we feel that the upside of the change overwhelms the downside. Nevertheless, at least for now, we are not treating it as a bug fix and therefore not planning to retrofit into the stable 6.4 release. Programmers concerned about whether this might affect them are advised to do a global source code search for occurrences of "[" within a subroutine or func/proc call parameter list, as well as within any "PUTARG" statement and then check to see whether the receiving variable is of type X and whether you are counting on the expression being terminated by the first null.
2014 September, A-Shell 6.1.1389: A-Shell now supports Passing DIMX Arrays to functions/procedures by reference.
Subtopics
•