1. Technology

Return Multiple Values From A Delphi Function

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

By

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. I guess that 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?

No. No, yes! :) I've been coding for quite a few years (decades) now and the first answer I would give would be "no" - simply because when I think of a function I think of a single return value.

Certainly the answer to the above question is: 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 - this means 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 - 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.

Honestly, I never use "var" parameters in normal functions / procedures. Not my way of coding - am not happy if some routine would alter the value of my local variable - as above is the case. I might use variable-by-reference parameters in event handling procedures - but only if needed.

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.

As I already said, I'm not a fan of such constructs. I 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.

©2014 About.com. All rights reserved.