How to Return Multiple Values From a Delphi Function

On Procedure/Function Parameters and Return Types: Var, Out, Record

A most common construct in a Delphi application would be a procedure or a function. Known as routines, procedures or functions are statement blocks you call from different locations in a program.

Simply put a procedure is a routine not returning a value while a function returns a value.

A return value from a function is defined by the return type. In most cases you would write a function to return a single value that would be an integer, string, boolean or some other simple type, also return types could be an array, a string list, an instance of a custom object or alike.

Note that even if your function returns a string list (a collection of strings) it still returns a single value: one instance of the string list.

Further, Delphi routines can really have many faces: Routine, Method, Method Pointer, Event Delegate, Anonymous method...

Can a Function Return Multiple Values?

The first answer that comes to mind is no, simply because when we think of a function, we think of a single return value.

Certainly, the answer to the above question is, however, yes. A function can return several values. Let's see how.

Var Parameters

How many values can the following function return, one or two?

function PositiveReciprocal(const valueIn : integer; var valueOut : real): boolean;

The function obviously returns a boolean value (true or false). How about the second parameter "valueOut" declared as a "VAR" (variable) parameter?

Var parameters are passed to the function by reference meaning that if the function changes the value of the parameter—a variable in the calling block of code—the function will change the value of the variable used for the parameter.

To see how the above works, here's the implementation:

function PositiveReciprocal(const valueIn: integer; var valueOut: real): boolean;

begin

 result := valueIn > 0;

 if result then valueOut := 1 / valueIn;

end;

The "valueIn" is passed as a constant parameter—function cannot alter it, and it is treated as read-only.

If "valueIn" or greater than zero, the "valueOut" parameter is assigned the reciprocal value of "valueIn" and the result of the function is true. If valueIn is <= 0 then the function returns false and "valueOut" is not altered in any way.

Here's the usage:

var

 b : boolean;

 r : real;

begin

 r := 5;

 b := PositiveReciprocal(1, r);

 //here:

 // b = true (since 1 >= 0)

 // r = 0.2 (1/5)

 r := 5;

 b := PositiveReciprocal(-1, r);

 //here:

 // b = false (since -1 

end;

Therefore, the PositiveReciprocal actually can "return" 2 values! Using var parameters you can have a routine return more than one value.

Out Parameters

There's another way to specify a by-reference parameter—using the "out" keyword, as in:

function PositiveReciprocalOut(const valueIn: integer; out valueOut: real): boolean;

begin

 result := valueIn > 0;

 if result then valueOut := 1 / valueIn;

end;

The implementation of PositiveReciprocalOut is the same as in PositiveReciprocal, there's only one difference: the "valueOut" is an OUT parameter.

With parameters declared as "out", the initial value of the referenced variable "valueOut" is discarded.

Here's the usage and the results:

var

 b : boolean;

 r : real;

begin

 r := 5;

 b := PositiveReciprocalOut(1, r);

 //here:

 // b = true (since 1 >= 0)

 // r = 0.2 (1/5)

 r := 5;

 b := PositiveReciprocalOut(-1, r);

 //here:

 // b = false (since -1 

end;

Note how in the second call the value of the local variable "r" is set to "0". The value of "r" was set to 5 before the function call but since the parameter in declared as "out," when "r" reached the function the value was discarded and the default "empty" value was set for the parameter (0 for real type).

As a result, you can safely send uninitialized variables for out parameters—something that you should not do with "var" parameters. Parameters are used to send something to the routine, except here with "out" parameters :), and therefore uninitialized variables (used for VAR parameters) could have weird values.

Returning Records?

The above implementations where a function would return more than one value are not nice. The function actually returns a single value, but also returns, better to say alters, the values of the var/out parameters.

Because of this, you may very rarely want to use by-reference parameters. If more results from a function are required, you can have a function return a record type variable.

Consider the following:

type

 TLatitudeLongitude = record

 Latitude: real;

 Longitude: real;

 end;

and a hypothetical function:

function WhereAmI(const townName : string) : TLatitudeLongitude;

The function WhereAmI would return the Latitude and Longitude for a given town (city, area, ...).

The implementation would be:

function WhereAmI(const townName: string): TLatitudeLongitude;

begin//use some service to locate "townName", then assign function result:

 result.Latitude := 45.54;

 result.Longitude := 18.71;

end;

And here we have a function returning 2 real values. Ok, it does return 1 record, but this record has 2 fields. Note that you can have a very complex record mixing various types to be returned as a result of a function.

That's it. Therefore, yes, Delphi functions can return multiple values.