Declare a function name and an optional list of local variable names to the compiler. When used inside a FoxPro DEFINE CLASS .. ENDDEFINE this declares a method.
[Attributes] [Modifiers] FUNCTION <idFunction>
[Typeparameters]
[([<idParam> [AS | REF|OUT|IN <idType>] [, ...])]
[AS <idType>]
[TypeparameterConstraints]
[<idConvention>]
[EXPORT LOCAL]
[DLLEXPORT STRING_CONST]
[=> <expression>]
CRLF
[<Body>]
[ENDFUNC | END FUNCTION]
Attributes | An optional list of one or more attributes that describe meta information for am entity, such as for example the [TestMethod] attribute on a method/function containing tests in a MsTest class library. Please note that Attributes must be on the same line or suffixed with a semi colon when they are written on the line above that keyword. |
Modifiers | An optional list of modifiers that specify the visibility or scope of the entity, such as PUBLIC, STATIC, INTERNAL, EXPORT and UNSAFE. |
Please note that functions and procedures used as class members in FoxPro compatible classes can have more modifiers. |
<idFunction> | A valid identifier name for the function. A function is an entity and, as such, shares the same name space as other entities. This means that it is not possible to have a function and a class, for example, with the same name. |
TypeParameters | This is supported for methods with generic type arguments. This something like <T> for a method with one type parameter named T. Usually one of the parameters in the parameter list is then also of type T. |
<idParam> | A parameter variable. A variable specified in this manner is automatically declared local. These variables, also called formal parameters, are used to receive arguments that you pass when you call the entity. |
AS | REF|OUT|IN <idType> | Specifies the data type of the parameter variable (called strong typing). AS indicates that the parameter must be passed by value, and REF indicates that it must be passed by reference with the @ operator. OUT is a special kind of REF parameter that does not have to be assigned before the call and must be assigned inside the body of the entity. IN parameters are passed as READONLY references. The last parameter in the list can also be declared as PARAMS <idType>[] which will tell the compiler that the function/method may receive zero or more optional parameters. Functions or Methods of the CLIPPER calling convention are compiled to a function with a single parameter that this declared as Args PARAMS USUAL[] |
AS <idType> | Specifies the data type. If omitted, then depending on the compiler options the type will be either USUAL or determined by the compiler. |
TypeParameterConstraints | Here you can specify constraints for the Type parameters, such as WHERE T IS SomeName or WHERE T IS New |
<idConvention> | Specifies the calling convention for this entity. <idConvention> must be one of the following: |
o CLIPPER
o STRICT
o PASCAL
o CALLBACK
o THISCALL
Most calling conventions are for backward compatibility only. There are 2 exceptions: CLIPPER declares that a method has untyped parameters. This is usually only needed for methods without any declared parameters. Otherwise the compiler will assume CLIPPER calling convention when it detects untyped parameters. Methods and Functions in external DLL may have STRICT, PASCAL, CALLBACK |
EXPORT LOCAL | This clause is allowed by X# but ignored. |
=> <Expression> | Single expression that replaces the multiline body for the entity. CANNOT be compiled with a body |
<Body> | Program statements that form the code of this entity. The <Body> can contain one or more RETURN statements to return control to the calling routine and to serve as the function return value. If no return statement is specified, control passes back to the calling routine when the function definition ends, and the function will return a default value depending on the return value data type specified (NIL if the return value is not strongly typed). CANNOT be combined with an Expression Body |
ENDFUNC | END FUNCTION | These (optional)keywords indicate the logical end of the function. |
A function is a subprogram comprised of a set of declarations and statements to be executed whenever you refer to <idFunction> followed by a pair of parentheses (see Notes section, below).
Functions and procedures (see the PROCEDURE statement in this guide) are the basic procedural programming units. You will use them in your applications to organize computational blocks of code.
STATIC FUNCTION allows you to limit the visibility of a function name to the current module, thereby restricting access to the function. This feature is useful when designing a module that will contain some public routines (i.e., with application-wide visibility) and others that are strictly support routines (i.e., only needed by other routines in the same module).
Simply declare all support functions using STATIC FUNCTION. Doing this gives you two immediate advantages. First, no other module in the application will inadvertently call one of your support routines. Second, since static references are resolved at compile time and public references are resolved at link time, there is no possibility of a name conflict. For example, if you have a static Service() function declared in module X and a public Service() function declared in module Y, all references to Service() in X execute the static version and all other references to Service() in the application execute the public version.
The Start() function: All applications must either have one function or procedure named Start() or be linked with the GUI Classes library and have a method Start() of CLASS App. Start() serves as the startup routine when the application is executed. Start() cannot declare any parameters and, under normal circumstances, should not return a value. If you want to use strong typing in the declaration statement, you must specify AS USUAL PASCAL.
Exporting locals through code blocks: When you create a code block, you can access local variables defined in the creating entity within the code block definition without having to pass them as parameters (i.e., local variables are visible to the code block). Using this fact along with the fact that you can pass a code block as a parameter, you can export local variables. For example:
FUNCTION One() EXPORT LOCAL
LOCAL nVar := 10 AS INT, cbAdd AS CODEBLOCK
cbAdd := {|nValue| nValue + nVar}
? NextFunc(cbAdd) // Result: 210
FUNCTION NextFunc(cbAddEmUp)
RETURN (EVAL(cbAddEmUp, 200))
When the code block is evaluated in NextFunc(), nVar, which is local to function One(), becomes visible even though it is not passed directly as a parameter.
Calling a function: The syntax to call a function is as follows:
<idFunction>([<uArgList>])
where <uArgList> is an optional comma-separated list of arguments to pass to the named function. The function receives the arguments in the order passed using the parameter variables specified as part of the function declaration.
Note that although the parentheses are not required in the FUNCTION statement if the function has no parameters, they are always required in the invocation.
You can call a function within an expression or as a program statement. If called as a program statement, the return value is ignored.
You can also call a function as an aliased expression, as in:
<idAlias>-><idFunction>([<uArgList>])
When you do this, the work area associated with <idAlias> is selected, the function is executed, and the original work area is reselected. You can specify an aliased expression as a program statement, as you would any other expression.
A function can call itself recursively. This means you can refer to a function in its own <FunctionBody>.
The specific manner in which you call a function depends on the calling convention (<idConvention>) that you specify (either explicitly or implicitly) when you declare the function.
CLIPPER calling convention: If you declare the function without any data types in the parameter list, the function uses the CLIPPER calling convention by default. You can also specify the CLIPPER calling convention in the FUNCTION declaration statement, providing that you do not use strong typing in the parameter list.
Although it does not allow strongly typed parameters, the CLIPPER calling convention supports strong typing of the function return value.
With the CLIPPER calling convention, the number of parameters declared for the function does not have to match the number of arguments passed when you call the function. You can skip any argument by leaving it out of the list (specifying two consecutive commas) or by omitting it from the end of the list. For example:
FUNCTION Start()
MyFunc(1,, 3) // Skip second argument
MyFunc(1, 2) // Skip final argument
FUNCTION MyFunc(x, y, z)
...
A parameter not receiving a value is automatically initialized to NIL by the function so that you can check for skipped arguments. You can use PCount() to help determine the number of arguments passed — this function returns the position of the last argument passed.
Any parameter specified in a CLIPPER function can receive arguments passed by value or reference — the semantics are determined when the function is called rather than when it is declared. The default method for expressions and variables is by value. All variables except field variables, when prefaced with the reference operator (@), are passed by reference. Field variables cannot be passed by reference and are always passed by value.
STRICT calling convention: If you declare the function with any data types in the parameter list, the function uses the STRICT calling convention by default. You can also specify the STRICT calling convention in the FUNCTION declaration statement.
Using the STRICT calling convention, you give up many of the features allowed with the CLIPPER calling convention, but you gain in compilation speed, application integrity, and execution speed by strongly typing the parameters and return value and declaring the passing semantics of the function.
STRICT functions do not support a variable number of arguments, PCount(), or the ability to be used in macro expressions.
Like CLIPPER functions, STRICT functions allow the calling semantics to be determined when the function is called, but only for polymorphic parameters (i.e., those not strongly typed). When a parameter is typed, the calling semantics are also declared depending on whether you use the AS or the REF keyword. AS means that the parameter must be passed by value and REF means that it must be passed by reference (with the reference operator (@)).
PASCAL calling convention: To specify this calling convention, use PASCAL as the last keyword in the FUNCTION declaration statement. Syntactically, the PASCAL calling convention is identical to STRICT and the usage restrictions are the same, but internally it is handled differently. It is identical to the Microsoft Pascal calling convention, and its primary use is for low-level interfacing with Windows.
CALLBACK calling convention: To specify this calling convention, use CALLBACK as the last keyword in the FUNCTION declaration statement. This is a special PASCAL calling convention with Windows prologue and epilogue. It is used for low-level interfacing with Windows.
Parameters: As an alternative to specifying parameters in the FUNCTION declaration statement, you can use a PARAMETERS statement to specify them. This practice, however, is not recommended because it is less efficient and provides no compile-time integrity validation. See the PARAMETERS statement in this guide for more information.
This example demonstrates a function that formats numeric values as currency:
FUNCTION Start()
? Currency(1000) // Result: $1,000.00
FUNCTION Currency(nNum)
LOCAL cNum
IF nNum < 0
cNum := Transform(-1 * nNum, ;
"999,999,999,999.99")
cNum := PadL("($" + LTRIM(cNum)+ ")", ;
LEN(cNum))
ELSE
cNum := Transform(nNum, ;
"999,999,999,999.99")
cNum := PadL("$" + LTRIM(cNum), ;
LEN(cNum))
ENDIF
RETURN cNum
The next example demonstrates a function that takes a string formatted as a comma-separated list and returns an array with one element per item:
aList := ListAsArray("One, Two")
// Result: {"One", "Two"}
FUNCTION ListAsArray(cList)
LOCAL nPos
LOCAL aList := {} // Define an empty array
DO WHILE (nPos := AT(",", cList)) != 0
// Add a new element
AADD(aList, SUBSTR(cList, 1, nPos - 1))
cList := SUBSTR(cList, nPos + 1)
ENDDO
AADD(aList, cList)
RETURN aList // Return the array
This example checks for a skipped argument by comparing the parameter to NIL:
FUNCTION MyFunc(param1,param2,param3)
IF param2 = NIL
param2 := "default value"
ENDIF ...
Here the Currency() function (defined above) is used as an aliased expression:
USE invoices NEW
USE customer NEW
? Invoices->Currency(Amount)
FIELD, LOCAL, MEMVAR, METHOD, PROCEDURE, RETURN