Remark: In many of the subsequent paragraphs the words procedure and function will be used interchangeably. The statements made are valid for both, except when indicated otherwise.
See section Parameters for the list of parameters. A procedure declaration that is followed by a block implements the action of the procedure in that block. The following is a valid procedure :
Procedure declaration
Procedure DoSomething (Para : String); begin Writeln ('Got parameter : ',Para); Writeln ('Parameter in upper case : ',Upper(Para)); end;Note that it is possible that a procedure calls itself.
The result type of a function can be any previously declared type. contrary to Turbo pascal, where only simple types could be returned.
Function declaration
Constant parameters and variable parameters can also be untyped parameters if they have no type identifier.
Parameters
When you declare parameters as value parameters, the procedure gets a copy of the parameters that the calling block passes. Any modifications to these parameters are purely local to the procedure's block, and do not propagate back to the calling block. A block that wishes to call a procedure with value parameters must pass assignment compatible parameters to the procedure. This means that the types should not match exactly, but can be converted (conversion code is inserted by the compiler itself)
Value parameters
Take care that using value parameters makes heavy use of the stack, especially if you pass large parameters. The total size of all parameters in the formal parameter list should be below 32K for portability's sake (the Intel version limits this to 64K).
You can pass open arrays as value parameters. See section openarray for more information on using open arrays.
When you declare parameters as variable parameters, the procedure or function accesses immediatly the variable that the calling block passed in its parameter list. The procedure gets a pointer to the variable that was passed, and uses this pointer to access the variable's value. From this, it follows that any changes that you make to the parameter, will proagate back to the calling block. This mechanism can be used to pass values back in procedures. Because of this, the calling block must pass a parameter of exactly the same type as the declared parameter's type. If it does not, the compiler will generate an error.
Variable parameters
Variable parameters can be untyped. In that case the variable has no type, and hence is incompatible with all other types. However, you can use the address operator on it, or you can pass it to a function that has also an untyped parameter. If you want to use an untyped parameter in an assigment, or you want to assign to it, you must use a typecast.
File type variables must always be passed as variable parameters.
You can pass open arrays as variable parameters. See section openarray for more information on using open arrays.
A constant argument is passed by reference if it's size is larger than a longint. It is passed by value if the size equals 4 or less. This means that the function or procedure receives a pointer to the passed argument, but you are not allowed to assign to it, this will result in a compiler error. Likewise, you cannot pass a const parameter on to another function that requires a variable parameter. The main use for this is reducing the stack size, hence improving performance, and still retaining the semantics of passing by value...
Constant parameters
Constant parameters can also be untyped. See section varparams for more information about untyped parameters.
You can pass open arrays as constant parameters. See section openarray for more information on using open arrays.
Row : Array of Integer;would be equivalent to
Row : Array[0..N-1] of Integer;Where N would be the actual size of the array that is passed to the function. N-1 can be calculated as High(Row). Open parameters can be passed by value, by reference or as a constant parameter. In the latter cases the procedure receives a pointer to the actual array. In the former case, it receives a copy of the array. In a function or procedure, you can pass open arrays only to functions which are also declared with open arrays as parameters, not to functions or procedures which accept arrays of fixed length. The following is an example of a function using an open array:
Function Average (Row : Array of integer) : Real; Var I : longint; Temp : Real; begin Temp := Row[0]; For I := 1 to High(Row) do Temp := Temp + Row[i]; Average := Temp / (High(Row)+1); end;
This is a special case of the Open array construction, where you are allowed to pass any expression in an array to a function or procedure.
In the procedure, passed the arguments can be examined using a special record:
Type PVarRec = ^TVarRec; TVarRec = record case VType : Longint of vtInteger : (VInteger: Longint); vtBoolean : (VBoolean: Boolean); vtChar : (VChar: Char); vtExtended : (VExtended: PExtended); vtString : (VString: PShortString); vtPointer : (VPointer: Pointer); vtPChar : (VPChar: PChar); vtObject : (VObject: TObject); vtClass : (VClass: TClass); vtAnsiString : (VAnsiString: Pointer); vtWideString : (VWideString: Pointer); vtInt64 : (VInt64: PInt64); end;Inside the procedure body, the array of const is equivalent to an open array of TVarRec:
Procedure Testit (Args: Array of const); Var I : longint; begin If High(Args)<0 then begin Writeln ('No aguments'); exit; end; Writeln ('Got ',High(Args)+1,' arguments :'); For i:=0 to High(Args) do begin write ('Argument ',i,' has type '); case Args[i].vtype of vtinteger : Writeln ('Integer, Value :',args[i].vinteger); vtboolean : Writeln ('Boolean, Value :',args[i].vboolean); vtchar : Writeln ('Char, value : ',args[i].vchar); vtextended : Writeln ('Extended, value : ',args[i].VExtended^); vtString : Writeln ('ShortString, value :',args[i].VString^); vtPointer : Writeln ('Pointer, value : ',Longint(Args[i].VPointer)); vtPChar : Writeln ('PCHar, value : ',Args[i].VPChar); vtObject : Writeln ('Object, name : ',Args[i].VObject.Classname); vtClass : Writeln ('Class reference, name :',Args[i].VClass.Classname); vtAnsiString : Writeln ('AnsiString, value :',AnsiString(Args[I].VAnsiStr else Writeln ('(Unknown) : ',args[i].vtype); end; end; end;In your code, it is possible to pass an arbitrary array of elements to this procedure:
S:='Ansistring 1'; T:='AnsiString 2'; Testit ([]); Testit ([1,2]); Testit (['A','B']); Testit ([TRUE,FALSE,TRUE]); Testit (['String','Another string']); Testit ([S,T]) ; Testit ([P1,P2]); Testit ([@testit,Nil]); Testit ([ObjA,ObjB]); Testit ([1.234,1.234]); TestIt ([AClass]);
If the procedure is declared with the cdecl modifier, then the compiler will pass the array as a C compiler would pass it. This, in effect, emulates the C construct of a varable number of arguments, as the following example will show:
program testaocc; {$mode objfpc} Const P : Pchar = 'example'; Fmt : PChar = 'This %s uses printf to print numbers (%d) and strings.'#10; // Declaration of standard C function printf: procedure printf (fm : pchar; args : array of const);cdecl; external 'c'; begin printf(Fmt,[P,123]); end.Remark that this is not true for Delphi, so code relying on this feature will not be portable.
... Dec(Var I : Longint;decrement : Longint); Dec(Var I : Longint); Dec(Var I : Byte;decrement : Longint); Dec(Var I : Byte); ...When the compiler encounters a call to the dec function, it will first search which function it should use. It therefore checks the parameters in your function call, and looks if there is a function definition which matches the specified parameter list. If the compiler finds such a function, a call is inserted to that function. If no such function is found, a compiler error is generated. You cannot have overloaded functions that have a cdecl or export modifier (Technically, because these two modifiers prevent the mangling of the function name by the compiler).
Program testforward; Procedure First (n : longint); forward; Procedure Second; begin WriteLn ('In second. Calling first...'); First (1); end; Procedure First (n : longint); begin WriteLn ('First received : ',n); end; begin Second; end.You cannot define a function twice as forward (nor is there any reason why you would want to do that). Likewise, in units, you cannot have a forward declared function of a function that has been declared in the interface part. The interface declaration counts as a forward declaration. The following unit will give an error when compiled:
Unit testforward; interface Procedure First (n : longint); Procedure Second; implementation Procedure First (n : longint); forward; Procedure Second; begin WriteLn ('In second. Calling first...'); First (1); end; Procedure First (n : longint); begin WriteLn ('First received : ',n); end; end.
It replaces, in effect, the function or procedure code block. As such, it can be present only in an implementation block of a unit, or in a program. As an example:
External directive
program CmodDemo; {$Linklib c} Const P : PChar = 'This is fun !'; Function strlen (P : PChar) : Longint; cdecl; external; begin WriteLn ('Length of (',p,') : ',strlen(p)) end.
Remark: The parameters in our declaration of the external function should match exactly the ones in the declaration in the object file.
If the external modifier is followed by a string constant:
external 'lname';Then this tells the compiler that the function resides in library 'lname'. The compiler will then automatically link this library to your program.
You can also specify the name that the function has in the library:
external 'lname' name Fname;This tells the compiler that the function resides in library 'lname', but with name 'Fname'. The compiler will then automatically link this library to your program, and use the correct name for the function. Under WINDOWS and OS/2, you can also use the following form:
external 'lname' Index Ind;This tells the compiler that the function resides in library 'lname', but with index Ind. The compiler will then automatically link this library to your program, and use the correct index for the function.
Contrary to Delphi, the assembler keyword must be present to indicate an assembler function. For more information about assembler functions, see the chapter on using assembler in the Programmers' guide.
Assembler functions
Free Pascal doesn't support all Turbo Pascal modifiers, but does support a number of additional modifiers. They are used mainly for assembler and reference to C object files. More on the use of modifiers can be found in the Programmers' guide.
Modifiers
Unit someunit; interface Function First : Real; Implementation Function First : Real; begin First := 0; end; Function Second : Real; [Public]; begin Second := 1; end; end.If another program or unit uses this unit, it will not be able to use the function Second, since it isn't declared in the interface part. However, it will be possible to access the function Second at the assembly-language level, by using it's mangled name (see the Programmers' guide).
program CmodDemo; {$LINKLIB c} Const P : PChar = 'This is fun !'; Function strlen (P : PChar) : Longint; cdecl; external; begin WriteLn ('Length of (',p,') : ',strlen(p)) end.When compiling this, and linking to the C-library, you will be able to call the strlen function throughout your program. The external directive tells the compiler that the function resides in an external object filebrary (see ).
Remark: The parameters in our declaration of the C function should match exactly the ones in the declaration in C. Since C is case sensitive, this means also that the name of the function must be exactly the same. the Free Pascal compiler will use the name exactly as it is typed in the declaration.
function DoSquare (X : Longint) : Longint; export; begin ... end;The square brackets around the modifier are not allowed in this case.
Remark: as of version 0.9.8, Free Pascal supports the Delphi cdecl modifier. This modifier works in the same way as the export modifier. More information about these modifiers can be found in the Programmers' guide, in the section on the calling mechanism and the chapter on linking.
You should not need this modifier, except maybe when calling assembler code.
Program Aliases; Procedure Printit; [Alias : 'DOIT']; begin WriteLn ('In Printit (alias : "DOIT")'); end; begin asm call DOIT end; end.
Remark: the specified alias is inserted straight into the assembly code, thus it is case sensitive.
The Alias modifier, combined with the Public modifier, make a powerful tool for making externally accessible object files.