Function: Difference between revisions

From Lazarus wiki
Jump to navigationJump to search
(→‎return value: modeswitch)
No edit summary
Line 8: Line 8:


== return value ==
== return value ==
In addition to a normal procedure, a function's formal signature contains a return type:
In addition to a normal procedure, a function's formal signature contains a return [[Data type|type]]:
The formal parameter list has to be succeeded by a colon and return type.
The formal parameter list has to be succeeded by a [[Colon|colon]] and return type.
For instance the following function returns a [[Boolean|<syntaxhighlight lang="pascal" enclose="none">boolean</syntaxhighlight>]].
For instance the following function returns a [[Boolean|<syntaxhighlight lang="pascal" enclose="none">boolean</syntaxhighlight>]].
<syntaxhighlight lang="pascal">
<syntaxhighlight lang="pascal">
Line 29: Line 29:
end;
end;
</syntaxhighlight>
</syntaxhighlight>
If <syntaxhighlight lang="pascal" enclose="none">{$modeswitch result+}</syntaxhighlight>, which is set by [[Mode ObjFPC|<syntaxhighlight lang="pascal" enclose="none">{$mode objFPC}</syntaxhighlight>]] and [[Mode Delphi|<syntaxhighlight lang="pascal" enclose="none">{$mode Delphi}</syntaxhighlight>]], inside the implementation block the special identifier <syntaxhighlight lang="pascal" enclose="none">result</syntaxhighlight> is available, too:
If <syntaxhighlight lang="pascal" enclose="none">{$modeswitch result+}</syntaxhighlight>, which is set by [[Mode ObjFPC|<syntaxhighlight lang="pascal" enclose="none">{$mode objFPC}</syntaxhighlight>]] and [[Mode Delphi|<syntaxhighlight lang="pascal" enclose="none">{$mode Delphi}</syntaxhighlight>]], inside the implementation block the special [[Identifier|identifier]] <syntaxhighlight lang="pascal" enclose="none">result</syntaxhighlight> is available, too:
<syntaxhighlight lang="pascal" line start="13" highlight="4">
<syntaxhighlight lang="pascal" line start="13" highlight="4">
// using special `result` identifier
// using special `result` identifier
Line 47: Line 47:
end;
end;
</syntaxhighlight>
</syntaxhighlight>
In assembly language other rules apply.
In [[Assembly language|assembly language]] other rules apply.
If the return type is an integral value, the accumulator register is used, provided it fits in there:
If the return type is an integral value, the accumulator register is used, provided it fits in there:
<syntaxhighlight lang="pascal" line start="25" highlight="9,16,28">
<syntaxhighlight lang="pascal" line start="25" highlight="9,16,28">
Line 141: Line 141:


== see also ==
== see also ==
* [https://www.freepascal.org/docs-html/ref/refch14.html § “Using functions and procedures” in the Reference Guide]
* [https://www.freepascal.org/docs-html/ref/refch14.html § “Using functions and procedures” in the Reference Guide]
* [https://www.freepascal.org/docs-html/prog/progse10.html § “Intel 80x86 Inline assembler” in the Programmer's Guide]
* [https://www.freepascal.org/docs-html/prog/progse10.html § “Intel 80x86 Inline assembler” in the Programmer's Guide]
 
[[Category:Code]]

Revision as of 08:50, 9 February 2019

Deutsch (de) English (en) español (es) suomi (fi) français (fr) русский (ru)

A function is a routine that, in contrast to procedures, returns a value. A call of a function is virtually substituted by its return value. If the {$extendedSyntax} compiler switch state is off, function calls can not appear as non-productive statements, but have to be or be part of an expression.

The word function is a reserved word.

return value

In addition to a normal procedure, a function's formal signature contains a return type: The formal parameter list has to be succeeded by a colon and return type. For instance the following function returns a boolean.

function myFunction(const firstParameter: real): boolean;

When implementing functions there are several ways to define the function's return value.

program functionDemo(input, output, stderr);

{$mode objfpc}

// traditional syntax:
// the result is stored in the variable
// its name is the same as the function's
function myLine(const x: real): real;
begin
	myLine := 0.5 * x + 2;
end;

If {$modeswitch result+}, which is set by {$mode objFPC} and {$mode Delphi}, inside the implementation block the special identifier result is available, too:

// using special `result` identifier
function myParabola(const x: real): real;
begin
	result := sqr(x) - 1;
end;

Additionally, in {$mode objFPC} the routine exit will set the return value, too, and leave the stack frame. In the previous two examples further statements could have appeared, and they would have been executed, whilst after an exit the routine is done. This is the behavior a return statement in C or other programming languages has.

// using exit routine
function even(const x: longint): boolean;
begin
	exit(not odd(x));
end;

In assembly language other rules apply. If the return type is an integral value, the accumulator register is used, provided it fits in there:

// in assembly language:
// return type fits into a single register => use accumulator register
function zero(const x: int64): boolean;
{$ifdef CPUx86_64}
assembler; register;
{$asmMode intel}
asm
	// xor modifies flags => put it in front of test
	xor rax, rax     // rax := 0 [false]
	
	// examining the assembler output
	// we can verify x is stored in register rdi [i.e. not rax]
	test x, x        // x = 0 ?
	jnz @zero_done   // if x <> 0 then goto done
	
	inc rax          // rax := 1 [true]
@zero_done:
	// When you examine the assembler output
	// you will notice the compiler automatically inserts code
	// that moves the contents of rax to the right spot on the stack.
end;
{$else}
begin
	// NOTE: with optimization switches enabled
	//       the compiler produces with the following Pascal statement
	//       even shorter (and maybe faster) code
	//       than the assembler implementation above
	result := x = 0;
end;
{$endif}

Otherwise, depending on which {$asmMode} is active, the @result (Intel) or __result (AT&T) macro can be used.

type
	bodyAttributes = record
		surfaceArea: real;
		volume: real;
	end;

// in assembly language:
// return type doesn't fit into accumulator => @result macro gives address
function sphere(const radius: real): bodyAttributes;
{$ifdef CPUx86_64}
assembler;
{$asmMode intel}
const
	three: longint = 3;
	four: longint = 4;
var
	r: real;
asm
	pextrq r, radius, 0 // r := (@radius+0)^
	lea rax, @result    // rax := @result
	fld r               // radius
	
	fld st(0)           // radius radius
	fild four           // 4 radius radius
	fldpi               // pi 4 radius radius
	fmul                // 4*pi radius radius
	fxch                // radius 4*pi radius
	fld st(0)           // radius radius 4*pi radius
	fmul                // radius^2 4*pi radius
	fmul                // 4*pi*radius^2 radius
	fst [rax].bodyAttributes.surfaceArea
	
	fmul                // 4*pi*radius^3
	fild three          // 3 4*pi*radius^3
	fdivp               // 4/3*pi*radius^3
	fst [rax].bodyAttributes.volume
end;
{$else}
begin
	sphere.surfaceArea := 4 * pi() * sqr(radius);
	sphere.volume := 4 / 3 * pi() * sqr(radius) * abs(radius);
end;
{$endif}

Originally Pascal expected exact one assignment to the result variable (whichever is used). FPC however does not prohibit multiple assignments. It will emit a warning, if none of the possible result identifiers were used or the exit routine is not written.

Warning
Function result does not seem to be set
You can get this warning if the compiler thinks that a function return value is not set. This will not be displayed for assembler procedures, or procedures that contain assembler blocks.
begin
	writeLn(sphere(2.0).surfaceArea);
end.

see also