Subsections


8 Using functions and procedures

Free Pascal supports the use of functions and procedures, but with some extras: Function overloading is supported, as well as Const parameters and open arrays.

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.

1 Procedure declaration

A procedure declaration defines an identifier and associates it with a block of code. The procedure can then be called with a procedure statement.

Procedure declaration

\begin{syntdiag}\setlength{\sdmidskip}{.5em}\sffamily\sloppy \synt{procedure\ de...
...
\synt{procedure\ header} \lit* ;
\synt{subroutine\ block} \lit *;\end{syntdiag}

\begin{syntdiag}\setlength{\sdmidskip}{.5em}\sffamily\sloppy \synt{procedure\ he...
...meter\ list}
\begin{displaymath}\synt{modifiers} \end{displaymath}\end{syntdiag}

\begin{syntdiag}\setlength{\sdmidskip}{.5em}\sffamily\sloppy \synt{subroutine\ b...
...ynt{external\ directive}\\
\synt{asm\ block}\\
\lit*{forward}
\)\end{syntdiag}
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 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.

2 Function declaration

A function declaration defines an identifier and associates it with a block of code. The block of code will return a result. The function can then be called inside an expression, or with a procedure statement, if extended syntax is on.

Function declaration

\begin{syntdiag}\setlength{\sdmidskip}{.5em}\sffamily\sloppy \synt{function\ dec...
...}
\synt{function\ header} \lit* ;
\synt{subroutine\ block} \lit *;\end{syntdiag}

\begin{syntdiag}\setlength{\sdmidskip}{.5em}\sffamily\sloppy \synt{function\ hea...
...sult\ type}
\begin{displaymath}\synt{modifiers} \end{displaymath}\end{syntdiag}

\begin{syntdiag}\setlength{\sdmidskip}{.5em}\sffamily\sloppy \synt{subroutine\ b...
...ynt{external\ directive}\\
\synt{asm\ block}\\
\lit*{forward}
\)\end{syntdiag}
The result type of a function can be any previously declared type. contrary to Turbo pascal, where only simple types could be returned.


3 Parameter lists

When arguments must be passed to a function or procedure, these parameters must be declared in the formal parameter list of that function or procedure. The parameter list is a declaration of identifiers that can be referred to only in that procedure or function's block.

Parameters

\begin{syntdiag}\setlength{\sdmidskip}{.5em}\sffamily\sloppy \synt{formal\ param...
...t}
\lit*( \<[b] \synt{parameter\ declaration} \\ \lit* ; \> \lit*)\end{syntdiag}

\begin{syntdiag}\setlength{\sdmidskip}{.5em}\sffamily\sloppy \synt{parameter\ de...
...er}\\
\synt{variable\ parameter}\\
\synt{constant\ parameter}
\)\end{syntdiag}
Constant parameters and variable parameters can also be untyped parameters if they have no type identifier.

1 Value parameters

Value parameters are declared as follows:

Value parameters

\begin{syntdiag}\setlength{\sdmidskip}{.5em}\sffamily\sloppy \synt{value\ parame...
...ath}\lit*{array} \lit*{of} \end{displaymath}\synt{parameter\ type}\end{syntdiag}
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)

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.


2 Variable parameters

Variable parameters are declared as follows:

Variable parameters

\begin{syntdiag}\setlength{\sdmidskip}{.5em}\sffamily\sloppy \synt{variable\ par...
...lit*{of}
\end{displaymath}\synt{parameter\ type}
\end{displaymath}\end{syntdiag}
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 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.

3 Constant parameters

In addition to variable parameters and value parameters Free Pascal also supports Constant parameters. A constant parameter as can be specified as follows:

Constant parameters


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


4 Open array parameters

Free Pascal supports the passing of open arrays, i.e. a procedure can be declared with an array of unspecified length as a parameter, as in Delphi. Open array parameters can be accessed in the procedure or function as an array that is declared with starting index 0, and last element index High(paremeter). For example, the parameter
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;

5 Array of const

In Object Pascal or Delphi mode, Free Pascal supports the Array of Const construction to pass parameters to a subroutine.

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.

4 Function overloading

Function overloading simply means that the same function is defined more than once, but each time with a different formal parameter list. The parameter lists must differ at least in one of it's elements type. When the compiler encounters a function call, it will look at the function parameters to decide which one of the defined functions it should call. This can be useful when the same function must be defined for different types. For example, in the RTL, the Dec procedure could be defined as:
...
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).

5 Forward defined functions

A function can be declared without having it followed by it's implementation, by having it followed by the forward procedure. The effective implementation of that function must follow later in the module. The function can be used after a forward declaration as if it had been implemented already. The following is an example of a forward declaration.
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.


6 External functions

The external modifier can be used to declare a function that resides in an external object file. It allows to use the function in some code, and at linking time, the object file containing the implementation of the function or procedure must be linked in.

External directive


It replaces, in effect, the function or procedure code block. As an example:
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.

7 Assembler functions

Functions and procedures can be completely implemented in assembly language. To indicate this, use the assembler keyword:

Assembler functions


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.

8 Modifiers

A function or procedure declaration can contain modifiers. Here we list the various possibilities:

Modifiers

\begin{syntdiag}\setlength{\sdmidskip}{.5em}\sffamily\sloppy \synt{modifiers}
\<...
...g\ constant}\\
\lit*{interrupt} \\
\lit*{call\ modifiers}
\)\>\end{syntdiag}

\begin{syntdiag}\setlength{\sdmidskip}{.5em}\sffamily\sloppy \synt{call\ modifie...
...ck}\\
\lit*{saveregisters}\\
\lit*{inline}\\
\lit*{safecall}
\)\end{syntdiag}
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.

1 alias

The alias modifier allows the programmer to specify a different name for a procedure or function. This is mostly useful for referring to this procedure from assembly language constructs or from another object file. As an example, consider the following program:
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.


2 cdecl

The cdecl modifier can be used to declare a function that uses a C type calling convention. This must be used when accessing functions residing in an object file generated by standard C compilers. It allows to use the function in the code, and at linking time, the object file containing the C implementation of the function or procedure must be linked in. As an example:
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.

3 export

The export modifier is used to export names when creating a shared library or an executable program. This means that the symbol will be publicly available, and can be imported from other programs. For more information on this modifier, consult the section on Programming dynamic libraries in the Programmers' guide.


4 inline

Procedures that are declared inline are copied to the places where they are called. This has the effect that there is no actual procedure call, the code of the procedure is just copied to where the procedure is needed, this results in faster execution speed if the function or procedure is used a lot.

By default, inline procedures are not allowed. Inline code must be enabled using the command-line switch -Si or {$inline on} directive.

  1. Inline code is NOT exported from a unit. This means that when calling an inline procedure from another unit, a normal procedure call will be performed. Only inside units, Inline procedures are really inlined.
  2. Recursive inline functions are not allowed. i.e. an inline function that calls itself is not allowed.


5 interrupt

The interrupt keyword is used to declare a routine which will be used as an interrupt handler. On entry to this routine, all the registers will be saved and on exit, all registers will be restored and an interrupt or trap return will be executed (instead of the normal return from subroutine instruction).

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.


6 pascal

The pascal modifier can be used to declare a function that uses the classic pascal type calling convention (passing parameters from left to right). For more information on the pascal calling convention, consult the Programmers' guide.


7 popstack

Popstack does the same as cdecl, namely it tells the Free Pascal compiler that a function uses the C calling convention. In difference with the cdecl modifier, it still mangles the name of the function as it would for a normal pascal function. With popstack, functions can be called by their pascal names in a library.

8 public

The Public keyword is used to declare a function globally in a unit. This is useful if the function should not be accessible from the unit file (i.e. another unit/program using the unit doesn't see the function), but must be accessible from the object file. as an example:
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).


9 register

The register keyword is used for compatibility with Delphi. In version 1.0.x of the compiler, this directive has no effect on the generated code.

10 saveregisters

If this modifier is specified after a procedure or function, then the Free Pascal compiler will save all registers on procedure entry, and restore them when the procedure exits (except for registers where return values are stored).

This modifier is not used under normal circumstances, except maybe when calling assembler code.

11 safecall

This modifier ressembles closely the stdcall modifier. It sends parameters from right to left on the stack.

More information about this modifier can be found in the Programmers' guide, in the section on the calling mechanism and the chapter on linking.

12 stdcall

This modifier pushes the parameters from right to left on the stack, it also aligns all the parameters to a default alignment.

More information about this modifier can be found in the Programmers' guide, in the section on the calling mechanism and the chapter on linking.

9 Unsupported Turbo Pascal modifiers

The modifiers that exist in Turbo pascal, but aren't supported by Free Pascal, are listed in table (Modifs) .

Table: Unsupported modifiers
Modifier Why not supported ?
Near Free Pascal is a 32-bit compiler.
Far Free Pascal is a 32-bit compiler.



Free Pascal Compiler
2001-09-22