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 parameters are declared 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
Care must be taken when using value parameters: Value parameters makes heavy use of the stack, especially when using 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).
Open arrays can be passed as value parameters. See section openarray for more information on using open arrays.
When parameters are declared 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 made to the parameter, will propagate 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, the address operator can be used on it, or it can be can passed to a function that has also an untyped parameter. If an untyped parameter is used in an assigment, or a value must be assigned to it, a typecast must be used.
File type variables must always be passed as variable parameters.
Open arrays can be passed 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 it cannot be assigned to, this will result in a compiler error. Furthermore a const parameter cannot be passed 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.
Open arrays can be passed 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, open arrays can only be passed 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 it is 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 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 variable 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 a 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. functions that have a cdecl modifier cannot be overloaded. (Technically, because this modifier prevents 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.A function can be defined as forward only once. Likewise, in units, it is not allowed to 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 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 the program.
The name that the function has in the library can also be specified:
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 the program, and use the correct name for the function. Under WINDOWS and OS/2, the following form can also be used:
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 the program, and use the correct index for the function.
Finally, the external directive can be used to specify the external name of the function :
{$L myfunc.o} external name 'Fname';This tells the compiler that the function has the name 'Fname'. The correct library or object file (in this case myfunc.o) must still be linked. so that the function 'Fname' is included in the linking stage.
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.
Modifiers
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 does not make the symbol public to other modules, unless the routine is also declared in the interface part of a unit, or the public modifier is used to force it as public. Consider the following:
unit testalias; interface procedure testroutine; implementation procedure testroutine;alias:'ARoutine'; begin WriteLn('Hello world'); end; end.
This will make the routine testroutine available publicly to external object files uunder the label name ARoutine.
program CmodDemo; {$LINKLIB c} Const P : PChar = 'This is fun !'; Function strlen (P : PChar) : Longint; cdecl; external name 'strlen'; begin WriteLn ('Length of (',p,') : ',strlen(p)) end.When compiling this, and linking to the C-library, the strlen function can be called throughout the program. The external directive tells the compiler that the function resides in an external object filebrary with the 'strlen' name (see ).
Remark: The parameters in our declaration of the C function should match exactly the ones in the declaration in C.
By default, inline procedures are not allowed. Inline code must be enabled using the command-line switch -Si or {$inline on} directive.
On platforms where a return from interrupt does not exist, the normal exit code of routines will be done instead. For more information on the generated code, consult the Programmers' guide.
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).
This modifier is not used under normal circumstances, except maybe when calling assembler code.
More information about this modifier can be found in the Programmers' guide, in the section on the calling mechanism and the chapter on linking.
More information about this modifier can be found in the Programmers' guide, in the section on the calling mechanism and the chapter on linking.