Function

From Free Pascal wiki
Jump to: navigation, search

Deutsch (de) English (en) español (es) 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.

  1. program functionDemo(input, output, stderr);
  2.  
  3. {$mode objfpc}
  4.  
  5. // traditional syntax:
  6. // the result is stored in the variable
  7. // its name is the same as the function's
  8. function myLine(const x: real): real;
  9. begin
  10. 	myLine := 0.5 * x + 2;
  11. end;

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

  1. // using special `result` identifier
  2. function myParabola(const x: real): real;
  3. begin
  4. 	result := sqr(x) - 1;
  5. 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.

  1. // using exit routine
  2. function even(const x: longint): boolean;
  3. begin
  4. 	exit(not odd(x));
  5. 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:

  1. // in assembly language:
  2. // return type fits into a single register => use accumulator register
  3. function zero(const x: int64): boolean;
  4. {$ifdef CPUx86_64}
  5. assembler; register;
  6. {$asmMode intel}
  7. asm
  8. 	// xor modifies flags => put it in front of test
  9. 	xor rax, rax     // rax := 0 [false]
  10.  
  11. 	// examining the assembler output
  12. 	// we can verify x is stored in register rdi [i.e. not rax]
  13. 	test x, x        // x = 0 ?
  14. 	jnz @zero_done   // if x <> 0 then goto done
  15.  
  16. 	inc rax          // rax := 1 [true]
  17. @zero_done:
  18. 	// When you examine the assembler output
  19. 	// you will notice the compiler automatically inserts code
  20. 	// that moves the contents of rax to the right spot on the stack.
  21. end;
  22. {$else}
  23. begin
  24. 	// NOTE: with optimization switches enabled
  25. 	//       the compiler produces with the following Pascal statement
  26. 	//       even shorter (and maybe faster) code
  27. 	//       than the assembler implementation above
  28. 	result := x = 0;
  29. end;
  30. {$endif}

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

  1. type
  2. 	bodyAttributes = record
  3. 		surfaceArea: real;
  4. 		volume: real;
  5. 	end;
  6.  
  7. // in assembly language:
  8. // return type doesn't fit into accumulator => @result macro gives address
  9. function sphere(const radius: real): bodyAttributes;
  10. {$ifdef CPUx86_64}
  11. assembler;
  12. {$asmMode intel}
  13. const
  14. 	three: longint = 3;
  15. 	four: longint = 4;
  16. var
  17. 	r: real;
  18. asm
  19. 	pextrq r, radius, 0 // r := (@radius+0)^
  20. 	lea rax, @result    // rax := @result
  21. 	fld r               // radius
  22.  
  23. 	fld st(0)           // radius radius
  24. 	fild four           // 4 radius radius
  25. 	fldpi               // pi 4 radius radius
  26. 	fmul                // 4*pi radius radius
  27. 	fxch                // radius 4*pi radius
  28. 	fld st(0)           // radius radius 4*pi radius
  29. 	fmul                // radius^2 4*pi radius
  30. 	fmul                // 4*pi*radius^2 radius
  31. 	fst [rax].bodyAttributes.surfaceArea
  32.  
  33. 	fmul                // 4*pi*radius^3
  34. 	fild three          // 3 4*pi*radius^3
  35. 	fdivp               // 4/3*pi*radius^3
  36. 	fst [rax].bodyAttributes.volume
  37. end;
  38. {$else}
  39. begin
  40. 	sphere.surfaceArea := 4 * pi() * sqr(radius);
  41. 	sphere.volume := 4 / 3 * pi() * sqr(radius) * abs(radius);
  42. end;
  43. {$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.
  1. begin
  2. 	writeLn(sphere(2.0).surfaceArea);
  3. end.

see also