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.

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

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


8.3 Parameter lists

When you need to pass arguments 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.

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

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.


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

8.3.3 Constant parameters

In addition to variable parameters and value parameters Free Pascal also supports Constant parameters. You can specify a constant parameter as follows:

Constant parameters

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


8.3.4 Open array parameters

Free Pascal supports the passing of open arrays, i.e. you can declare a procedure 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, 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;

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

8.4 Function overloading

Function overloading simply means that you can define the same function 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 if you want to define the same function for different types. For example, in the RTL, the Dec procedure is is 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 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).

8.5 Forward defined functions

You can define a function 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.
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.


8.6 External functions

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

External directive

\begin{syntdiag}\setlength{\sdmidskip}{.5em}\sffamily\sloppy \synt{external\ dir...
...ndex} \synt{integer\ constant}
\end{displaymath}\end{displaymath}\end{syntdiag}
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:
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.

8.7 Assembler functions

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

Assembler functions

\begin{syntdiag}\setlength{\sdmidskip}{.5em}\sffamily\sloppy \synt{asm\ block}
\lit*{assembler} \lit*;
\synt{declaration\ part}
\synt{asm\ statement}\end{syntdiag}
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.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...
...l}\\
\lit*{stdcall}\\
\lit*{popstack}\\
\lit*{saveregisters}
\)\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. More on the use of modifiers can be found in the Programmers' guide.

8.8.1 Public

The Public keyword is used to declare a function globally in a unit. This is useful if you don't want a function to be accessible from the unit file, but you do want the function to 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).


8.8.2 cdecl

The cdecl modifier can be used to declare a function that uses a C type calling convention. This must be used if you wish to acces functions in an object file generated by a C compiler. It allows you to use the function in your code, and at linking time, you must link the object file containing the C implementation of the function or procedure. 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.
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.


8.8.3 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 you could access functions by their pascal names in a library.

8.8.4 Export

Sometimes you must provide a callback function for a C library, or you want your routines to be callable from a C program. Since Free Pascal and C use different calling schemes for functions and procedures8.1, the compiler must be told to generate code that can be called from a C routine. This is where the Export modifier comes in. Contrary to the other modifiers, it must be specified separately, as follows:
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.

8.8.5 StdCall

As of version 0.9.8, Free Pascal supports the Delphi stdcall modifier. This modifier does actually nothing, since the Free Pascal compiler by default pushes parameters from right to left on the stack, which is what the modifier does under Delphi (which pushes parameters on the stack from left to right). More information about this modifier can be found in the Programmers' guide, in the section on the calling mechanism and the chapter on linking.

8.8.6 saveregisters

As of version 0.99.15, Free Pascal has the saveregisters modifier. 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).

You should not need this modifier, except maybe when calling assembler code.

8.8.7 Alias

The Alias modifier allows you to specify a different name for a procedure or function. This is mostly useful for referring to this procedure from assembly language constructs. 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, combined with the Public modifier, make a powerful tool for making externally accessible object files.

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



root
2000-12-20