Function: what changed
Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.
Title: Function
| Old | New | Line | |
|---|---|---|---|
| 1 | + | A **function** is a [[Routine|routine]] that, in contrast to [[Procedure|procedures]], returns a value. A call of a function is virtually substituted by its return value. If the [[$extendedSyntax|{$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]]. | |
| 2 | + | ||
| 3 | + | The word `function` is a [[Reserved word|reserved word]]. | |
| 4 | + | ||
| 5 | + | ## Return value | |
| 6 | + | ||
| 7 | + | 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|colon]] and return type. For instance the following function returns a [[Boolean]]. | |
| 8 | + | ||
| 9 | + | ``` pascal | |
| 10 | + | function myFunction(const firstParameter: real): Boolean; | |
| 11 | + | ``` | |
| 12 | + | ||
| 13 | + | When implementing functions there are several ways to define the function’s return value. | |
| 14 | + | ||
| 15 | + | ``` pascal | |
| 16 | + | program functionDemo(input, output, stderr); | |
| 17 | + | ||
| 18 | + | {$mode objFPC} | |
| 19 | + | ||
| 20 | + | // traditional syntax: | |
| 21 | + | // the result is stored in the variable | |
| 22 | + | // its name is the same as the function's | |
| 23 | + | function myLine(const x: real): real; | |
| 24 | + | begin | |
| 25 | + | myLine := 0.5 * x + 2; | |
| 26 | + | end; | |
| 27 | + | ``` | |
| 28 | + | ||
| 29 | + | If `{$modeswitch result+}`, which is set by [[Mode ObjFPC|{$mode objFPC}]] and [[Mode Delphi|{$mode Delphi}]], inside the implementation block the special [[Identifier|identifier]] `result` is available, too: | |
| 30 | + | ||
| 31 | + | ``` pascal | |
| 32 | + | // using special `result` identifier | |
| 33 | + | function myParabola(const x: real): real; | |
| 34 | + | begin | |
| 35 | + | result := sqr(x) - 1; | |
| 36 | + | end; | |
| 37 | + | ``` | |
| 38 | + | ||
| 39 | + | Additionally, in `{$mode objFPC}` the routine [`exit`](https://www.freepascal.org/docs-html/rtl/system/exit.html) 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. | |
| 40 | + | ||
| 41 | + | ``` pascal | |
| 42 | + | // using exit routine | |
| 43 | + | function even(const x: longint): Boolean; | |
| 44 | + | begin | |
| 45 | + | exit(not odd(x)); | |
| 46 | + | end; | |
| 47 | + | ``` | |
| 48 | + | ||
| 49 | + | 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: | |
| 50 | + | ||
| 51 | + | ``` pascal | |
| 52 | + | // in assembly language: | |
| 53 | + | // return type fits into a single register => use accumulator register | |
| 54 | + | function zero(const x: int64): Boolean; | |
| 55 | + | {$ifDef CPUx86_64} | |
| 56 | + | assembler; register; | |
| 57 | + | {$asmMode intel} | |
| 58 | + | asm | |
| 59 | + | // xor modifies flags => put it in front of test | |
| 60 | + | xor rax, rax // rax := 0 (remove residue) | |
| 61 | + | ||
| 62 | + | test x, x // x = 0 ? | |
| 63 | + | setz al // rax := ZF | |
| 64 | + | ||
| 65 | + | // When you examine the assembler output | |
| 66 | + | // you will notice the compiler automatically inserts code | |
| 67 | + | // that moves the contents of rax to the right spot on the stack, | |
| 68 | + | // unless the noStackFrame hint | |
| 69 | + | // (automatically set by some optimization levels) | |
| 70 | + | // instructs the compiler to omit the stack frame if possible. | |
| 71 | + | end; | |
| 72 | + | {$else} | |
| 73 | + | begin | |
| 74 | + | result := x = 0; | |
| 75 | + | end; | |
| 76 | + | {$endIf} | |
| 77 | + | ``` | |
| 78 | + | ||
| 79 | + | Otherwise, depending on which [[sAsmmode|{$asmMode}]] is active, the `@result` (Intel) or `__result` (AT&T) macro can be used. | |
| 80 | + | ||
| 81 | + | ``` pascal | |
| 82 | + | type | |
| 83 | + | bodyAttributes = record | |
| 84 | + | surfaceArea: real; | |
| 85 | + | volume: real; | |
| 86 | + | end; | |
| 87 | + | ||
| 88 | + | // in assembly language: | |
| 89 | + | // return type doesn't fit into accumulator => @result macro gives address | |
| 90 | + | function sphere(const radius: real): bodyAttributes; | |
| 91 | + | {$ifDef CPUx86_64} | |
| 92 | + | assembler; | |
| 93 | + | {$asmMode intel} | |
| 94 | + | const | |
| 95 | + | three: longint = 3; | |
| 96 | + | four: longint = 4; | |
| 97 | + | var | |
| 98 | + | r: real; | |
| 99 | + | asm | |
| 100 | + | pextrq r, radius, 0 // r := (@radius+0)^ | |
| 101 | + | lea rax, @result // rax := @result | |
| 102 | + | fld r // radius | |
| 103 | + | ||
| 104 | + | fld st(0) // radius radius | |
| 105 | + | fild four // 4 radius radius | |
| 106 | + | fldpi // pi 4 radius radius | |
| 107 | + | fmul // 4*pi radius radius | |
| 108 | + | fxch // radius 4*pi radius | |
| 109 | + | fld st(0) // radius radius 4*pi radius | |
| 110 | + | fmul // radius^2 4*pi radius | |
| 111 | + | fmul // 4*pi*radius^2 radius | |
| 112 | + | fst [rax].bodyAttributes.surfaceArea | |
| 113 | + | ||
| 114 | + | fmul // 4*pi*radius^3 | |
| 115 | + | fild three // 3 4*pi*radius^3 | |
| 116 | + | fdivp // 4/3*pi*radius^3 | |
| 117 | + | fst [rax].bodyAttributes.volume | |
| 118 | + | end; | |
| 119 | + | {$else} | |
| 120 | + | begin | |
| 121 | + | sphere.surfaceArea := 4 * pi() * sqr(radius); | |
| 122 | + | sphere.volume := 4 / 3 * pi() * sqr(radius) * abs(radius); | |
| 123 | + | end; | |
| 124 | + | {$endIf} | |
| 125 | + | ``` | |
| 126 | + | ||
| 127 | + | Originally Pascal expected *exact one* [[Becomes|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. | |
| 128 | + | ||
| 129 | + | > Warning | |
| 130 | + | > Function result does not seem to be set | |
| 131 | + | > | |
| 132 | + | > You can get this warning if the [[Compiler|compiler]] thinks that a function return value is not set. This will not be displayed for assembler procedures, or procedures that contain assembler blocks. | |
| 133 | + | ||
| 134 | + | ``` pascal | |
| 135 | + | begin | |
| 136 | + | writeLn(sphere(2.0).surfaceArea); | |
| 137 | + | end. | |
| 138 | + | ``` | |
| 139 | + | ||
| 140 | + | Note, in the case of [[Operator overloading|operator overloads]], a special form of a function, a result variable identifier has to be declared in the formal signature. See the article for details. | |
| 141 | + | ||
| 142 | + | ## Remarks | |
| 143 | + | ||
| 144 | + | [[Pure functions]] are functions that do not depend on an external state. | |
| 145 | + | ||
| 146 | + | ## See also | |
| 147 | + | ||
| 148 | + | - [[Pascal basics]] | |
| 149 | + | - [§ “Using functions and procedures” in the Reference Guide](https://www.freepascal.org/docs-html/ref/refch14.html) | |
| 150 | + | - [§ “Intel 80x86 Inline assembler” in the Programmer’s Guide](https://www.freepascal.org/docs-html/prog/progse10.html) | |
| 151 | + | ||
| 152 | + | --- | |
| 153 | + | *From the Free Pascal wiki, page "Function", as it stood on 2021-02-20 after a change by Kai Burghardt, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).* |
