FUNCTION/END FUNCTION statements

Purpose

Define a Function block.

Syntax

[CALLBACK | STATIC] FUNCTION FuncName [BDECL | CDECL | SDECL] [ALIAS "AliasName"] [([arguments])] [EXPORT | PRIVATE] [STATIC] [AS type]

  [LOCAL variable list]

  [STATIC variable list]

  [DIM variable list]

  {statements}

  [{FuncName | FUNCTION} = ReturnValue]

  {statements}

  [EXIT FUNCTION]

  {statements}

END FUNCTION

Remarks

All executable code must reside in a Sub or Function block.  You cannot define a Function (or Sub) inside another Function or Sub.  The FUNCTION statement has the following parts:

STATIC

Specifies the default storage class for variables declared inside the Function If not specified, the default storage class is LOCAL.  The STATIC keyword may appear before the FUNCTION keyword, or after the list of arguments.

FuncName

The name of the Function.  A type-specifier may be appended (just like an ordinary variable name) to specify the data type of the Function's return value, in place of the [AS type] clause. FuncName must be unique: no other variable, Function, Sub, or label can share it.  Also see ALIAS below.

Future versions of PowerBASIC may not support type-specifier symbols for the Function return type, so specify the return data type with an explicit AS type clause in all DECLARE and FUNCTION definitions, to ensure future compatibility.

BDECL

Specifies that the declared procedure uses the BASIC/Pascal calling convention.  When a procedure calls a BDECL procedure, it passes its parameters on the stack from left to right. 

It is the responsibility of the called procedure to clean up the stack before returning to the calling procedure.  Therefore, all PowerBASIC Subs and Functions that specify the BDECL convention automatically clean up the stack before execution returns to the calling code.

CDECL

Specifies that the declared Function uses the C calling convention.  When a CDECL Function is called, it passes its parameters on the stack from right to left.

The calling procedure removes any passed parameters from the stack as part of the return process.  When PowerBASIC code calls Subs and Functions using the CDECL convention, the stack is cleaned automatically after execution returns from the called code.

In the event the called procedure is imported or exported, PowerBASIC will automatically create a C style ALIAS for the function name.  This alias will be prefixed with an underscore followed by the original function name converted to lowercase.

The following two declarations are equivalent, indicating how the default ALIAS name would be created by PowerBASIC:

FUNCTION C_Function CDECL () EXPORT AS LONG

FUNCTION C_Function CDECL ALIAS "_c_function" () EXPORT AS LONG

SDECL

This is the default if neither BDECL nor CDECL are specified.  SDECL (and its synonym STDCALL) specifies that the declared Function uses the "Standard Calling Convention" as defined by Microsoft.  When calling an SDECL Function, parameters are passed on the stack from right to left.

PowerBASIC Subs and Functions that use the SDECL/STDCALL convention automatically clean up the stack before execution returns to the calling code.

ALIAS

String literal that identifies an case-sensitive alternative name for the procedure.  This lets you export a Function (or Sub) by a name other than what it is called and referenced within the source code.

This can be useful if you want to abbreviate a long name, provide a more descriptive name, or if the exported name needs to contain characters that are illegal in PowerBASIC.  AliasName is the routine's actual name as it appears in the export table, and FuncName is the title that you can use in PowerBASIC.  For example:

FUNCTION ShortName ALIAS "LongFuncName"() EXPORT STATIC AS LONG

The ALIAS clause is very important when exporting Subs and Functions.  Omitting the ALIAS clause or incorrectly capitalizing the alias name are common causes of "Missing Export" errors.  Please refer to the DECLARE section for more information.

 

Passing parameters

arguments

An optional, comma-delimited sequence of formal parameters.  The parameters used in the arguments list serve only to define the Function; they have no relationship to other variables in the calling code with the same name.  The default variable scope within the Function body can be specified by including a type class keyword (STATIC, or LOCAL) at the end of the header; the default, if no keyword is included, is LOCAL.  Also see STATIC (above).

Normally, PowerBASIC passes parameters to a Function either by reference (BYREF) or by copy (BYCOPY).  Either way, the address of the variable is passed and the procedure has to look at that address to get the value of the parameter.  If you do not need to modify the parameters (true in many cases), you can speed up your calls by passing the parameters by value using the BYVAL keyword.

You can also force a parameter to be passed by reference (rather than by value or by copy) by using the BYREF keyword.

The type of the parameter is specified either by appending a type-specifier character to the name or by using an AS clause.  For example:

FUNCTION Test&(A AS INTEGER) 'integer passed by ref

FUNCTION Test&(A%)           'integer passed by ref

FUNCTION Test&(BYREF A%)     'integer passed by ref

FUNCTION Test&(BYVAL A%)     'integer passed by val

 

Parameter restrictions

 

PowerBASIC compilers have a limit of 32 parameters per FUNCTION.  To pass more than 32 parameters to a FUNCTION, construct a User-Defined Type (UDT) and pass 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.

PowerBASIC Functions cannot return an array, Variant, or Object variable as a Function return value.  Pass these variable types as BYREF parameters instead.  For example:

lResult& = ProcessData(TheArray&(), iSize%)

...

FUNCTION ProcessData(lArr() AS LONG, iSize%) AS LONG

  REDIM lArr(iSize%) AS LONG

  lArr(iSize%) = 1&

  FUNCTION = -1&

END FUNCTION

 

Pointer parameters

 

When a 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.

The AS clause is required when declaring a pointer as a parameter.  Each pointer parameter must be declared to be passed BYVAL.  The use of the BYVAL keyword in the calling code is optional but recommended for clarity:

' Integer Pointer (passed by value)

FUNCTION Test(BYVAL A AS INTEGER PTR) AS LONG

This allows the parameter to be accessed indirectly using the at sign '@' de-referencing prefix as in:

@A = 56

Additional information on BYVAL/BYREF/BYCOPY parameter passing can be found in the CALL statement topic.

 

Optional parameters

 

PowerBASIC now supports two syntax formats for optional parameters: the classic optional parameter syntax using brackets "[..]", and the new syntax using the OPTIONAL (or OPT) keyword.  We'll discuss each one in turn.

 

Using OPTIONAL/OPT

 

FUNCTION statements may specify one or more parameters as optional by preceding the parameter with either the keyword OPTIONAL (or the abbreviation OPT).  Optional parameters are only allowed with CDECL or SDECL calling conventions, not BDECL.

When a parameter is declared optional, all subsequent parameters in the declaration are optional as well, whether or not they specify an explicit OPTIONAL or OPT directive.  The following two lines are equivalent, with both second and third parameters being optional:

FUNCTION sABC(a&, OPTIONAL BYVAL b&, OPTIONAL BYVAL c&) AS LONG

FUNCTION sABC(a&, OPT BYVAL b&, BYVAL c&) AS LONG

When optional parameters are omitted in the calling code, the stack area normally reserved for those parameters is zero-filled.  This allows you to test if an optional parameter was passed or not:

If the parameter is defined as a BYVAL parameter, it will have the value zero.  For TYPE or UNION variables passed BYVAL, the compiler will pass a string of binary zeroes of length SIZEOF(Type_or_union_var).

If the parameter is defined as a BYREF parameter, VARPTR (varname) will equal zero; when this is true, any attempt to use Var_name in your code will result in Error #9 (null pointer); failure to detect this error using error-trapping may result in a General Protection Fault or memory corruption.

The OPTIONAL directive provides the same functionality as the older syntax using square brackets "[..]".  See below.

 

Using classic optional parameters

 

When declaring a CDECL SUB or FUNCTION, you can specify trailing parameters as optional, using a set of brackets [..]:

FUNCTION KerPlunk CDECL (x%, y% [, z%]) AS LONG

Note that the comma separating the y% parameter from the optional z% parameter is inside the brackets.  The following calling sequences would then be valid:

x& = KerPlunk(x%, y%)

x& = KerPlunk(x%, y%, z%)

Optional parameters must be the last parameters designated in the list.  The following is invalid:

FUNCTION KerPlunk CDECL ([x%,] y%, z%) AS LONG

Because the FUNCTION (or SUB) being called does not know how many parameters are being passed at the time it is called, you should pass the number of parameters as one of the required parameters in the list.

PowerBASIC continues to  support the use of classic optional parameter syntax using brackets ([..]) but this will not be the case in future versions of PowerBASIC.  Existing code should be changed to the new OPTIONAL syntax as soon as possible to ensure compatibility with future versions of PowerBASIC.

EXPORT

Functions are private by default.  The EXPORT keyword can be used to make a Function accessible from another module.  Exported Subs and Functions can only be imported from a DLL, not from another .EXE.

PRIVATE

Functions are private by default.  The PRIVATE keyword is not compulsory, but may be used to help make your code more easily read.

AS type

You may specify the type of data returned by a Function to the calling code.  If you do not specify a type, PowerBASIC assumes that the Function returns the data type specified by a DEFtype statement.  However, if no DEFtype or AS type has been specified, a compile-time error is generated.

Therefore, there are two ways to specify the return type of a Function:

  •  Include a type-specifier character at the end of FuncName

  •  Include the AS type clause as the last part of the FUNCTION statement (this is the recommended syntax to ensure compatibility with future versions of PowerBASIC).

For example, the following statements are equivalent:

FUNCTION aFunction?()

FUNCTION aFunction() AS BYTE

 

Assigning a return value

 

You can specify the return value of the Function by explicitly setting the value, either by assigning a value to the FUNCTION keyword, or by assigning a value to the function name.  For example, the two lines within the following Function block are equivalent:

FUNCTION AddData() AS LONG

  ...

  AddData  = 123& ' Assign value to function name

  FUNCTION = 123& ' Assign value to the function

END FUNCTION

 

Default return value

 

If the code within the Function does not explicitly set a return value, the default return value will be zero if the function returns a numeric data type, or an empty string if the function returns a string.  For example:

FUNCTION AddData() AS LONG

  ...

  IF condition THEN

    EXIT FUNCTION  ' No assignment, will return 0&

  ELSE

    FUNCTION = -1& ' An explicit return value

  END IF

END FUNCTION

PowerBASIC Functions cannot return an array as a Function return value.  Pass the array as a parameter instead.  For example:

lResult& = CheckTheData(InTheArray&())

...

FUNCTION CheckTheData(lArr() AS LONG) AS LONG

  ...

END FUNCTION

 

Variables within functions

 

The default scope for variables within Functions is LOCAL.  Placing the STATIC keyword before the FUNCTION keyword forces all variables inside the Function to default to STATIC scope.

Function blocks are constructed very much like Subs (see SUB/END  SUB statement).  However, Functions differ from Subs in that they can be used in expressions, and they return a value simply by assigning it to the Function name (or with the FUNCTION = statement; see above).

Functions cannot return fixed-length strings, structures, or whole arrays.  You may return a pointer to these data types, however.  If doing so, the target variable should be declared as STATIC or GLOBAL.

LOCAL variables are created within the Sub/Function's stack frame.  If a LOCAL variable exceeds the amount of stack space available, it may become necessary to use a STATIC or GLOBAL variable instead.  For example, creating a LOCAL ASCIIZ or LOCAL fixed-length string that is very large (say, approaching 1 Mb) can trigger a General Protection Fault (GPF) because it may overrun the stack frame.

See also

DECLARE, EXIT, FUNCNAME$, GLOBAL, LOCAL, STATIC, SUB/END SUB, THREAD CREATE, THREADID

Example

FUNCTION HalfOf ALIAS "HalfOf" (X!) EXPORT AS SINGLE

  FUNCTION = X! / 2

END FUNCTION