On the “out” parameter specifier in Delphi

8

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

Class Methods vs Instance Methods

9

C# implements BigInteger.Parse as a static class method, and there is obvious reason for it: you can call a static class methods in a variable’s declaration:

  BigInteger N = BigInteger.Parse(stringToParse);

Delphi/Pascal does not support the above syntax, so the equivalent code is

var
  N: BigInteger;
begin
  N:= BigInteger.Parse(stringToParse);

But now it looks like implementing BigInteger.Parse as a static class method is nothing but additional typing; using an instance method looks better:

var
  N: BigInteger;
begin
  N.Parse(stringToParse);

So, what is the right “Delphi way” of implementing BigInteger.Parse – as a static class method or as an instance method?

Why PDWORD is not a pointer to DWORD

2

Once upon a time the Delphi team lead decided that Delphi should contain declarations of C++ types like unsigned long for the sake of better C++ compatibility. So he has written

type
  CppULongInt = LongWord;

No, that is not good thought the team lead. Delphi’s LongWord is a 32-bit type while the C++’s unsigned long is a platform-dependent type. So let us declare CppULongInt as a distinct type instead of an alias type:

type
  CppULongInt = type LongWord;

That is better. Now how about DWORD type? It is declared in Windows headers as

  typedef unsigned long       DWORD;

So for compatibility sake we will declare DWORD as

type
  DWORD = CppULongInt;

Great, thought the Delphi team lead. He call two juniors (let us call them DevA and DevB) and said:

DevA, create a branch (BranchA) in the Delphi project, declare type DWORD = CppULongInt; and fix all bugs that may appear after;
DevB, create a branch (BranchB) in the Delphi project, declare type PDWORD = ^CppULongInt; and fix all bugs that may appear after;

After you both are ready we will merge the branches and declare PDWORD type as it should be:

type
  PDWORD = ^DWORD;

In a due time the happy DevB came to the Delphi team lead and said: “I did everything as you said Sir!. Now type DWORD = CppULongInt;, and everything is fine!”.

But the second guy DevA was not happy. He said: “Sir, we have tons of code like that:

procedure Foo(var Value: DWord);
begin
  Value:= 0;
end;

var
  D: Longword;

begin
  Foo(D);

If I declare

type
  DWORD = CppULongInt;

Then it does not compile. What should I do?”

-“Nothing”, said the Delphi team lead. “The BranchB will be the main trunk now”.

Disclaimer: the tale was written after reading this SO question

On the reference counting bugs in Delphi

8

Delphi allows to access an object instance via object reference and interface reference at the same time. Consider this code:

program RefCounts;

{$APPTYPE CONSOLE}

uses
  SysUtils;

procedure Test;
var
  Obj: TInterfacedObject;
  II: IInterface;

begin
  Obj:= TInterfacedObject.Create;
  Writeln(Obj.RefCount);             // 0
  II:= Obj;
  Writeln(Obj.RefCount);             // 1
  II:= nil;
// here Obj is already destroyed and we can't reference it anymore
//  Writeln(Obj.RefCount);
end;

begin
  Test;
  Readln;
end.

Nasty thing is that a reference-counted object is created with zero reference counter and can be destroyed by means of an interface reference. Needless to say that combining object and interface references is bad practice and should be avoided. Unfortunately the reference counting (with zero counters allowed) appeared to be a difficult problem for the compiler too and resulted in compiler bugs even when the code compiled does not use object references (see example here). The bugs were getting fixed with newer Delphi versions but nobody can say which Delphi version is free from them.

On the future Delphi type system

6

Recent post by Allen Bauer announced that some future Delphi release will introduce “rooted” type system so that every type will be derived from TObject. It does not mean that plain old types like Integer will actually be implemented as TObject descendants. As far as I could understand code like this

Var I: LongInt;
  ...
  I:= 2;
  I:= I * 4;
  ...

will treat the variable I as a plain 32-bit value as before, but code like this

Var I: LongInt;
    S: string;
  ...
  I:= 2;
  S:= I.ToString;
  ...

will probably create a “wrapper” object for integer type internally to implement ToString method.

I want to make two remarks to the above.

The first is about terminology. I never seen it explained in Delphi books or documentation but well, instance of every type is an object. Code

Var I: Integer;

declares object of integer type. There are two different kinds of type – value types and reference types. Byte, Word, Integer, record and many more Delphi types are value types. A variable of a value type is an object itself. Reference types in Delphi are string, dynamic array, class and interface. A variable of reference type is a pointer to object.

Second, rooted type system is not the only possible approach to bring order into the type system. The concept that makes you stunned at the first acquaintance with Haskell is “type classes”. In Haskell every type is an instance of a type class; and a type can be an instance of many type classes at once. For example Integer type in Haskell is an instance of “show” type class, so integers can be converted to strings; Integer type is also an instance of “read” type class so strings can be converted to integers.

On the Pointers and References

6

A Reference is a distinct concept in C/C++ languages. The next code sample (MinGW GCC compiler was used)

#include <iostream>

int main()
  {
    int Value;
    int &ValueRef = Value;

    Value = 2;
    std::cout << "Value: " << Value << "\n";
    std::cout << "ValueRef: " << ValueRef << "\n";

    ValueRef = 3;
    std::cout << "Value: " << Value << "\n";
    std::cout << "ValueRef: " << ValueRef << "\n";
    return 0;
  }

declares ValueRef as a reference to Value variable. The output is
_ref1
Though ValueRef variable is a pointer to Value internally the indirection operator is never used, and the syntax for accessing Value directly or indirectly via ValueRef is the same. ValueRef is also called an alias to Value; the point that ValueRef variable is a pointer internally is just an implementation detail.

Another important thing about C/C++ references is that they are always initialized. The language syntax enforces that you cannot declare a wild reference.

Pascal does not have the same reference concept. The closest concept is a procedure parameter passed by reference:

program ref;

{$APPTYPE CONSOLE}

var
  Value: Integer;

procedure Test(var ValueRef: Integer);
begin
  Writeln('ValueRef: ', ValueRef);
  ValueRef:= 3;
end;

begin
  Value:= 2;
  Writeln('Value: ', Value);
  Test(Value);

  Writeln('Value: ', Value);
  Test(Value);
end.

we can see that

  • ValueRef does not use indirection operator to access a referenced variable;
  • the language syntax enforces that ValueRef is always initialized.

Delphi does not elaborate the reference concept, though there are many built-in types in the language that are ‘transparent pointers’ – objects, interfaces, dynamic arrays, strings. The term reference can be used for example for object variables because the language syntax makes these variables indistinguishable from referenced instances. Instead the term object is usually used, that can mean object reference or object instance, so sometimes you think twice to understand what a particular object does mean.

Bitwise operations on big integers

0

Standard fixed-sized negative integers are stored in two’s complement format; for arbitrary-precision big integers the two’s complement format means infinite size, so internally it is not used. Still bitwise operation on big integers are implemented as if negative big integer values are stored in two’s complement format.

As a result bitwise boolean operations (and, or, xor) applied to big integers produce the same results as with standard fixed-sized integers:

procedure Test1(I, J: Integer);
var
  BigI, BigJ: BigInteger;

begin
  BigI:= I;
  BigJ:= J;
  Assert(BigI and BigJ = I and J);
  Assert(BigI or BigJ = I or J);
  Assert(BigI xor BigJ = I xor J);
end;

There is a difference between standard Delphi integer types and big integers in shift operations. Shift operations on big integers preserve sign. That means any shift applied to negative big integer results in negative big integer (the same is for non-negative values):

procedure Test2(I: BigInteger; N: Cardinal);
begin
  Assert(((I shl N) < 0) = (I < 0));
  Assert(((I shr N) < 0) = (I < 0));
end;

That is a natural consequence of the infinite-sized two’s complement negative values. Shift operations on big integers are arithmetic shifts rather than logical shifts. On the other hand ‘shl’ and ‘shr’ operations on the standard Delphi integer types are implemented as logical shifts and does not preserve sign:

procedure Test3;
var
  I: Integer;

begin
  I:= -1;
  Writeln(I shr 2);   // 1073741823, because 'shr' is logical shift
  I:= 1000000000;
  Writeln(I shl 2);   // -294967296, because of 32-bit overflow
end;

PS: right now TForge does not support bitwise operations on BigInteger type, they will be implemented soon.