CALL statement

Purpose | Invoke a procedure ( Sub, Function, Method, Property, or FastProc).
Syntax | CALL ProcName [([arguments])] [TO result_var]
Remarks | The CALL statement has the following parts:
ProcName | The name of a Sub, Function, Method, Property, or FastProc defined elsewhere in the program.

arguments

An optional, comma-delimited list of variables, expressions, and constants to be passed to the procedure as parameters, for up to 32 parameters. If the CALL keyword is used, the arguments must be enclosed in parentheses.

You can omit the CALL keyword. If you do so, you may also omit the parentheses surrounding arguments. For example, the following lines are equivalent:

CALL MyProc(parm1, parm2)

MyProc(parm1, Parm2)

MyProc parm1, parm2

However, if the first parameter argument is enclosed in parentheses for any reason, the entire parameter list must be enclosed in parentheses. For example:

MyProc (3+z, b) ' Valid syntax

MyProc ((3+z), b) ' Valid syntax

MyProc (3+z), b ' Invalid syntax

This updated syntax now permits macros to be called using the SUB-style convention if/when the macros expand directly to Function calls. For example:

MACRO sm(Msg) = SendMessage(a, Msg, b, c)

can be called like this (when the return value is not required):

sm(x)

In all cases, the number and type of parameters passed must agree with the arguments in procedure definition.

Variant Arguments

You can think of a Variant as a kind of container, which can hold a variable of most any data type. If you call a procedure which requires a variant for one or more of its arguments, PowerBASIC will automatically convert a standard data type into a variant data type.

While a variant may not normally contain a UDT, PowerBASIC offers a special methodology to do so. At programmer direction, a TYPE may be assigned to a variant (as a byte string) by using:

[LET] VrntVar = TypeVar AS STRING

In the same manner, a UDT argument can be auto-converted to the variant type by appending AS STRING:

CALL ProcName(UDTVar AS STRING)

The data contained in the User-Defined Type variable (UDT) is stored in the variant argument as a dynamic string of bytes (vt_bstr). When you retrieve that UDT data (with Variant$), PowerBASIC understands the content and handles it accurately. However, other programming languages may not understand this technique, so it should be limited to PowerBASIC applications. This methodology is implemented in all of the PowerBASIC COLLECTION objects as it greatly enhances ease of coding and performance of the final executable.

Passing Parameters

In a procedure definition, every parameter is described by the data type, and the format used to pass it. The type may be any normal variable type, such as long, string, User-Defined Type, etc. The passing format describes how the value is presented to the procedure: by reference (BYREF), by value (BYVAL), or by reference to a copy (BYCOPY).

BYREF | When a parameter is passed by reference, it consists of a 4-byte address of the data. In this case, the original data can be modified by the procedure.

BYVAL

When a parameter is passed by value, it consists of an actual copy of the data. Since the parameter is a copy, the original data cannot be modified by the procedure.

When you pass parameters from the calling code with an explicit BYVAL, you effectively switch off the compilers type-checking for that parameter. This can be useful in cases where the called code is expecting a BYREF parameter, and you wish to pass an address of another data type that would trigger a compile-time error without the BYVAL method. For example:

SUB TheSub(x AS STRINGZ) ' Address of x expected

[statements]

END SUB

[statements]

DIM a$

a$ = "Dynamic string data"

CALL TheSub(BYVAL STRPTR(a$)) ' Pass data address

BYCOPY

A parameter passed by copy is a special case; somewhat of a hybrid of the other two methods. When a procedure expects a parameter to be passed by reference, it expects to see a pointer to the data. In some cases, such as when the parameter is a calculated expression, it is not precisely possible to pass a pointer, since an expression result is a temporary value that does not exist in a permanent memory location. On the other hand, if you wish to ensure that the original data is not modified by the procedure, you can place a BYCOPY override in the arguments list.

In both cases, a copy of the data is stored in a temporary memory location, and the parameter consists of a 4-byte address of this temporary location. Another way to force BYCOPY is to enclose a variable name in parentheses, so it will appear to the compiler as an expression, rather than just a single variable.

Unless declared otherwise, parameters default to BYREF passing method. Expressions and constants are always passed BYCOPY. Fixed length strings, User-Defined Types, and full arrays are always passed BYREF.

CALL MySub (i&) ' i& is passed by reference

CALL MySub (BYREF i&) ' i& is passed by reference

CALL MySub (BYCOPY i&) ' i& is passed by copy

CALL MySub ((i&)) ' i& is passed by copy

Unless declared otherwise, parameters default to the BYREF passing method. Expressions and constants are always passed BYCOPY. Full arrays are always passed BYREF.

Entire arrays are specified by using an empty set of parentheses after the array, while individual array elements are specified by subscript index number. For example:

CALL SumArray(a()) ' pass entire array 'a'

CALL SumArray(a(3)) ' pass element 3 of array 'a'

The CALL statement can be used to invoke functions, subs, methods, properties, or fastprocs. In this case, the return value of the function is simply discarded, unless the TO keyword is used to specify a return variable.

If a procedure expects a parameter by reference, it is possible to substitute a pointer by value, for the identical result. This is particularly useful with Fixed-length strings and Types:

DECLARE SUB a(z%)

DIM MyInt AS INTEGER, x AS INTEGER PTR

x = VARPTR(MyInt)

CALL a(MyInt)

' or

CALL a(BYVAL x)

' or

CALL a(BYVAL VARPTR(MyInt))

Of course, if the procedure is expecting a parameter by value, you may not pass the pointer, but rather the pointer target (i.e., CALL a(@x)).

PowerBASIC compilers have a limit of 32 parameters per SUB, FUNCTION, METHOD, and PROPERTY. To pass more than 32 parameters, construct a User-Defined Type (UDT) and pass (the address of) the UDT by reference (BYREF) instead.

Fixed-length strings, STRINGZ strings, and User-Defined Types/Unions may also be passed as BYVAL or OPTIONAL parameters, now. Try to avoid passing large items BYVAL, as it's terribly inefficient, and there is a maximum size limit of 64 Kb for a given parameter list. Arrays cannot be passed BYVAL.

When a procedure definition specifies either a BYREF parameter or a pointer variable parameter, the calling code may freely pass a BYVAL DWORD or a Pointer instead. While the use of the explicit BYVAL override in the calling code is optional, it is recommended for clarity. It is necessary to explicitly declare all pointer parameters as BYVAL (i.e., BYVAL X AS BYTE PTR). Failure to do so will generate a compile-time Error 549 ("BYVAL required with pointers").

A procedure may also be imported and exported within the same module. That is, a function in the module may be stated as EXPORT, while a DECLARE in the same module specifies it as an imported function by the option LIB "XXX.DLL", provided that XXX.DLL is the name of the module. This may be particularly valuable when you wish to build an #INCLUDE file with all of the DECLARE statements for a project.

For information on using OPTIONAL parameters, please see DECLARE, FUNCTION, METHOD, PROPERTY and SUB topics.

NOTHING | The reserved word NOTHING can be used to replace any OBJECT variable parameter. In this case, the compiler passes a null object (or a pointer to a null object if BYREF) in place of a typical parameter. While this simplifies some programming issues, the technique must be used with caution. If the target METHOD or FUNCTION is not expecting a null parameter, it could cause a fatal error condition.
TO result_var | This offers an optional way to assign a function return value to result_var. For example, the following code assigns the return value to x% in two different ways: x% = MyFunCall CALL MyFunCall TO x%
Restrictions | A thread Function may not be directly called or executed, except by a THREAD CREATE statement.
See also | CALL DWORD, DECLARE, FASTPROC, FUNCTION/END FUNCTION, METHOD, PROPERTY, SUB/END SUB, THREAD CREATE