On the “out” parameter specifier in Delphi

I’ve posted before what the “out” parameter specifier is actually doing in a Delphi code. Now let us consider when the “out” keyword should be used instead of “var”.

There is only one case when the “out” keyword should be used. It is closely related to COM technology support, but it is not really limited to COM. The case is importing a function with a parameter of interface type which violates Delphi contract on reference counting. To illustrate the point consider the following DLL:

library DemoDll;

uses
  SysUtils;

type
  PMyUnknown = ^TMyUnknown;
  TMyUnknown = record
    FVTable: PPointer;
    FRefCount: Integer;
    class function QueryIntf(Inst: Pointer; const IID: TGUID;
                             out Obj): HRESULT; stdcall; static;
    class function AddRef(Inst: PMyUnknown): Integer; stdcall; static;
    class function Release(Inst: PMyUnknown): Integer; stdcall; static;
  end;

const
  VTable: array[0..2] of Pointer = (
    @TMyUnknown.QueryIntf,
    @TMyUnknown.Addref,
    @TMyUnknown.Release);

function ReleaseInstance(Inst: Pointer): Integer;
type
  TVTable = array[0..2] of Pointer;
  PVTable = ^TVTable;
  PPVTable = ^PVTable;

  TRelease = function(Inst: Pointer): Integer; stdcall;

begin
  Result:= TRelease(PPVTable(Inst)^^[2])(Inst);
end;

class function TMyUnknown.QueryIntf(Inst: Pointer; const IID: TGUID;
  out Obj): HRESULT;
begin
  Result:= E_NOINTERFACE;
end;

class function TMyUnknown.Addref(Inst: PMyUnknown): Integer;
begin
  Inc(Inst.FRefCount);
  Result:= Inst.FRefCount;
end;

class function TMyUnknown.Release(Inst: PMyUnknown): Integer;
begin
  Dec(Inst.FRefCount);
  Result:= Inst.FRefCount;
  if Result = 0 then
    FreeMem(Inst);
end;

procedure GetIUnknown1(var Inst: PMyUnknown);
var
  P: PMyUnknown;

begin
  New(P);
  P^.FVTable:= @VTable;
  P^.FRefCount:= 1;
  if Inst <> nil then ReleaseInstance(Inst);
  Inst:= P;
end;

procedure GetIUnknown2(var Inst: PMyUnknown);
begin
  New(Inst);
  Inst^.FVTable:= @VTable;
  Inst^.FRefCount:= 1;
end;

exports
  GetIUnknown1,
  GetIUnknown2;

{$R *.res}

begin
  ReportMemoryLeaksOnShutdown:= True;
end.

The DLL exports 2 procedures which create instances implementing IUnknown. The first procedure (GetIUnknown1) follows Delphi rules on reference counting, the second (GetIUnknown2) does not. To use the procedures in an executable we need an import unit:

unit DemoImport;

interface

procedure GetIUnknown1(var I: IUnknown);
//procedure GetIUnknown2(var I: IUnknown); // memory leak
procedure GetIUnknown2(out I: IUnknown);
//                     ^^^!
implementation

procedure GetIUnknown1(var I: IUnknown); external 'DemoDLL' name 'GetIUnknown1';
//procedure GetIUnknown2(var I: IUnknown); external 'DemoDLL' name 'GetIUnknown2';
procedure GetIUnknown2(out I: IUnknown); external 'DemoDLL' name 'GetIUnknown2';

end.

Note that the second procedure is imported with out parameter specifier; the result of replacing “out” by “var” is a memory leak which can be shown by the test application:

program DemoEXE;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  DemoImport in 'DemoImport.pas';

procedure Test;
var
  I: IUnknown;

begin
  GetIUnknown1(I);
  GetIUnknown2(I);
end;

begin
  Test;
  Readln;
end.
Advertisements

8 thoughts on “On the “out” parameter specifier in Delphi

  1. “There is only one case when the “out” keyword should be used.”

    I disagree – I use out all the time when the value going in is irrelevant and only the value coming back. If you use var for such cases you cannot tell from looking at the signature if the in value is important or not.

    • Read it the other way: if an interface parameter is declared with ‘out’ specifier, then no need to follow Delphi rules on reference counting in the procedure; though AFAIR the compiler follows them anyway.

  2. Agree with Stefan

    I use out parameters in functions like TryStrToInt: You return a True indicating that you could convert the string, and the value in a out param. Is that or replace with a ugly try-except block

  3. The problem with Delphi’s out is that most of the time it is indistinguishable from var. Apart from the times when it’s different. As shown here.

    So when we use out to indicate that the value on entry should not be used, the compiler doesn’t enforce it. In effect it is a comment masquerading as language syntax.

  4. VAR parameter must be initialized before call or you get a compiler warning. OUT does not need to be initialized.
    Other case is webservice client generation where multiple OUT parameter needed.
    Also it has affet on interface reference count (function result and OUT parameter handles refCount differently)
    It’s very good for find/search and conversion methos where the result is boolean, and the OUT parameter contains the valid data ICO true result.

  5. “out” ensures that any variable of a “managed” type (string, dyn-array, interface, variant, etc…) or even a record containing a managed type is *finalize* before the call. You kind of “hinted” at why it is needed, but not what the actual rules are. Yes, “out” is like a “var” when the type is unmanaged, but that is not the point. Look at the declaration for QueryInterface on IInterface. Notice that the “out” parameter is untyped. This is intentional, because that allows any interface variable to be passed in *and* ensure it is properly finalized prior to the call.

    So the rule is simple. Aside from being more clear when reading the code, it also has a very clear semantic meaning where any variable that is managed will always be finalized prior to the call.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s