21 March 2018

Function and Sub parameters

In the new English Html help additional information is provided for Function and Sub. Since this is new information a copy of the text has a place in a blogpost.

Function parameters
The return value of a Function can be assigned to a local variable with the same name as the Function. When the return type is a numeric data type a local variable of that type is automatically added to the function’s local variables. With String, Type (UDT) and Variant as the return type a by reference variable is passed as the last argument on the stack. The string, UDT or variant becomes the variable that can be used to pass the function’s return value. Therefor, the following is equal:

Dim h$
h$ = testf(8) ' assign result to h$
testp(8, h$)  ' put result in h$
Function testf(a%) As String testf = "3" & a%
Procedure testp(a%, ByRef p$) p$ = "3" & a%

In the function testf the variable h$ is silently passed on the stack. Inside the function this by reference variable is known as testf. Assigning a new string to testf actually assigns the string to h$ directly.

When a Function is used for a Windows API callback make sure the return data type is a primary numeric type (Byte, Word, Long, Int64, Single, Double), otherwise the stack will be overwritten.

Sub And FunctionVar parameters
For compatibility reasons GFA-BASIC 32 includes the Sub and FunctionVar statements. FunctionVar is compatible with VB’s Function; arguments are passed by reference by default and without a data type the Variant type is assumed. The same is true for Sub, without a ByVal or ByRef keyword the default is by reference, an implicit by reference. When a datatype is missing the Variant type is the default. Examples:

Sub test(vnt)        ' implicit ByRef, Variant datatype
  vnt = "new Value"  ' do not write to parameter

FunctionVar tfv(a As String) ' As Variant
  tfv = "new value" + s

Although ByRef is implied the rules aren’t as strict as with an explicit ByRef. When ByRef is included only actual variables can be passed, without the ByRef keyword the Sub and FunctionVar also accept literal values and types that don’t match. The following calls are allowed:

Local vnt As Variant, s As String
test(7)     ' 7 is assigned to a local Variant first
test(vnt)   ' vnt variable is passed by ref
test(s)     ' types don't match
vnt = tfv(7)' 7 is assigned to string first

Note that the literal value 7 is passed to the by reference parameter vnt in test. The compiler won’t complain since an implicit by reference does not have to reference an actual application varaible. When the Sub test(vnt) and FunctionVar tfv() include a ByRef keyword explicitly the compiler will check the argument’s type against the parameter’s type and will complain if they don’t match. An explicit ByRef declaration only accepts actual variables of the same type as the argument. Both, the variable that is passed as the argument, and the by reference parameter must be of the same type, like this

Dim s As String
CallByRef s     ' must be a variable
Debug s         ' = “new value”
Sub CallByRef(ByRef p As String)
  ' only accepts String variables as arguments
  p = "new value"
EndSub

Limits to the use of parameters
So, there is a subtle difference between the default, an implicit ByRef and an explicit ByRef in a Sub and FunctionVar. In both cases only a variable can be passed by reference properly. With an implicit by reference an actual variable can be passed only when the types match. In all other circumstances the argument is first copied to a hidden local variable of the same datatype as the parameter and then the hidden variable is passed by reference. In the example above, the literal value 7 is first copied to a temporary hidden Variant variable and then the temporary variable is passed by reference. The same is true for the third call: test(s). Since the data types don’t match the string s is first copied to a temporary Variant variable which in turn is then passed to test(). The temporary hidden variable is immediately destroyed after returning from the Sub or FunctionVar.

Now lets look at it from the Sub’s point of view. Although by reference is implied the Sub doesn’t know what kind of variable is actually passed. It might be an actual variable, but it might also be a reference to a temporary local variable. Therefore Sub and FunctionVar cannot return a value using an implicit by reference parameter. In case a temporary variable is passed, it will be destroyed immediately after returning from the Sub. Only when a parameter is declared using the ByRef keyword explicitly is the Sub guaranteed to receive an actual variable.

Note An implicit ByRef parameter cannot be used as a local variable as can with ByVal parameters. The Sub doesn’t know whether the parameter is a reference to a temporary variable or to an actual variable. In case the parameter references an actual application variable the Sub might very well overwrite the contents of that variable.


No comments:

Post a Comment