CALL statement

Purpose

Invoke a procedure (Sub or Function).

Syntax

CALL ProcName [([arguments])] [TO result_var]

Remarks

The CALL statement has the following parts:

ProcName

The name of a Sub or Function 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.  If no DECLARE statement is provided, the CALL keyword may be used to forward reference a SUB or FUNCTION that appears physically later in the source code than does the calling code.

As long as ProcName is a PowerBASIC procedure, or is an external procedure declared with the DECLARE statement, you can omit the CALL keyword.  If you do so, you may 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.

Passing parameters

In a procedure definition, every parameter is described by type and according to the way it is passed to the procedure.  The type may be any normal variable type, such as Integer, Floating Point, String, User-Defined Type, etc.  The passing method 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 ASCIIZ) ' Address of x expected

  ...

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.

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

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, as well as SUBs.  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 SUB/FUNCTION 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 or FUNCTION.  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, ASCIIZ 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 Sub/Function 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 Sub/Function 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, and SUB topics.

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, FUNCTION/END FUNCTION, SUB/END SUB, THREAD CREATE