Friday 1 September 2017

Smart pointers

Introduction

Today, I had a discussion on StackOverflow (I hope no one deleted it yet) on the usefulness of smart pointers instead of try...finally.

My arguments are as follows:

1. try...finally decreases the readability of a function, especially if they are nested, e.g. when you instantiate a TStream and then a TStringList. Then you would need two nested try...finally constructs, thoroughly decreasing readability.

2. The code that destroys (frees) the object can be several lines removed from the place where it is created. Smart pointers allow you to take care of lifetime management right where you create the object.

3. Even if you are very good at manual memory management, taking care of it is still a chore, and it is always nice to have something, like ARC, or smart pointers, that can take that away from you. It greatly reduces the number of situations where you can make mistakes.

4. No matter how good you are, you can always forget to call Free, or call LongNamedObjectA.Free when you actually meant LongNamedObjectAB.Free. Such errors are hard to find, especially if they are several lines away from the actual creation spot. Using smartpointers, you don't have to repeat yourself this way, so you can't make such mistakes.

So if people tell you that only lazy or incompetent people would use smart pointers, or that people won't be able to understand your code if you use them, don't believe it. They greatly reduce the chance to make mistakes, even if you are competent at manual memory and lifetime management. They remove a lot of clutter and make your code more readable and let you concentrate on what you want to do, not on lifetime management anymore.

Implementation

I have seen quite a few smart pointer implementations. The nice thing about them is that they take care of automatically destroying the "guarded" object, so you don't have to take care of that yourself. So instead of something like:

var
  X: TMyObject;
begin
  X := TMyObject.Create(17);
  try
    // Several lines of code using X
  finally
    X.Free;
  end;
end;

Now should be able to do something like:

var
  X: TMyObject;
begin
  X := SmartPointer<TMyObject>(TMyObject.Create(17));
  // Several lines of code using X
end;

And at the end of the function, X is freed automatically.

Awkward to use

Unlike what I want to have, most implementations I have seen require you to declare the smart pointers as extra variables:

var
  X: TMyObject;
  SP: SmartPointer<TMyObject>;
begin
  X := SmartPointer<TMyObject>(SP, TMyObject.Create(17));
  // Several lines of code using X
end;

This is so for the ObjectHandle by Barry Kelly, as well as the ISafeGuard types in the JCL. But I never liked that. It makes you repeat yourself needlessly and makes the code less readable than it could be, taking away one of the advantages smart pointers provide, in my opinion.

Trick

So I employ a few tricks. The first one is that if a function or constructor returns a record, and this record is never assigned to a variable, the compiler still puts an anonymous record on the stack. That is because, for any record that is larger than a register, a record that is returned is actually passed as a var parameter. So the following:

function Bla(I, J: Integer): TMyRecord;

is actually compiled as:

procedure Bla(I, J: Integer; var Result: TMyRecord);

But that means that an actual record must be passed. If, in your code that uses the function, you don't assign the result of the function, the compiler creates an anonymous record on the stack and passes that, so inside your function you can assign values to the fields of Result.

Note that the use of the anonymous record mechanism is safe. It is also employed for intermediate results of class operators like + or *. If I use my BigIntegers, or Delphi's sample TComplex, then D := A + B * C; actually produces the code AnonymousIntermediate := op_Multiply(B, C); D := op_Add(A, AnonymousIntermediate); It will not change anytime soon.

Here comes the second trick: I want to return a TMyObject, and not a smart pointer record, so I can assign the result directly to X. For this, I (ab)use the Implicit operators you can define for a record. I define a

class operator Implicit(A: TMyObject): SmartPointer<TMyObject>;
and a
class operator Implicit(A: SmartPointer<TMyObject>): TMyObject;
So if I do the following:

X := SmartPointer<TMyObject>(TMyObject.Create(17));

then first, the first Implicit operator returns a record (a smart pointer). This is an anonymous record. But since X is a TMyObject, subsequently the other Implicit operator converts the anonymous record into a TMyObject again. How this "conversion" (actually just some passing around of a reference) is done can be seen in the code a bit further on.

So now I don't have to explicitly declare any smart pointers and can return and assign the created object in one fell swoop. The only thing to take care of is making this generic. That is what I have done in the following simple unit.

Many years ago, I implemented the original smart pointers in the JCL and called them Guards, and I want to remain faithful to that name. But since they are a little "smarter" and easier to use then those early implementations (which did not have overloaded operators or records with methods yet), I called them SmartGuards instead.

The unit

unit Velthuis.SmartGuards;

interface

type
  IGuard = interface
  ['{CE522D5D-41DE-4C6F-BC84-912C2AEF66B3}']
  end;

  TGuard = class(TInterfacedObject, IGuard)
  private
    FObject: TObject;
  public
    constructor Create(AObject: TObject);
    destructor Destroy; override;
  end;

  SmartGuard<T: class> = record
  private
    FGuard: IGuard;
    FGuardedObject: T;
  public
    class operator Implicit(GuardedObject: T): SmartGuard<T>;
    class operator Implicit(Guard: SmartGuard<T>): T;
  end;

implementation

{ TGuard }

constructor TGuard.Create(AObject: TObject);
begin
  FObject := AObject;
end;

destructor TGuard.Destroy;
begin
  FObject.Free;
  inherited;
end;

{ SmartGuard }

class operator SmartGuard<T>.Implicit(GuardedObject: T): SmartGuard<T>;
begin
  Result.FGuard := TGuard.Create(GuardedObject);
  Result.FGuardedObject := GuardedObject;
end;

class operator SmartGuard<T>.Implicit(Guard: SmartGuard<T>): T;
begin
  Result := Guard.FGuardedObject;
end;

end.

Sample code

Here is a simple (braindead?) program that uses them. It defines a simple but talkative class that shows when it is being destroyed, and which has a name. The demo shows how it can be used with the SmartGuard.

program SmartGuardDemo;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  Velthuis.SmartGuards in 'Velthuis.SmartGuards.pas';

type
  TTalker = class
  private
    FName: string;
  public
    constructor Create(const Name: string);
    destructor Destroy; override;
    procedure Talk;
  end;

{ TTalker }

constructor TTalker.Create(const Name: string);
begin
  FName := Name;
end;

destructor TTalker.Destroy;
begin
  Writeln(FName, ' is being destroyed');
  inherited;
end;

procedure TTalker.Talk;
begin
  Writeln(FName, ' is talking');
end;

procedure Test;
var
  A, B: TTalker;
  I: Integer;
begin
  Writeln('Creating object London...');
  A := SmartGuard<TTalker>(TTalker.Create('London'));
  Writeln('Creating object Paris...');
  B := SmartGuard<TTalker>(TTalker.Create('Paris'));
  A.Talk;
  B.Talk;
  Writeln('OK, so they talked');
  for I := 1 to 100 do
    Write(I:8);
  Writeln;
  Writeln;
  Writeln('Leaving the test');
end;

begin
  Test;
  Readln;
end.

Conclusion

I am sure that these smart pointers could do with some enhancements, and that there could be smart pointers for plain pointers/memory blocks too, or smart pointers that can be stored in lists, arrays, etc. and keep their guarded objects alive for longer than a function. This is just the base frame I have been thinking of.

As was said in the StackOverflow discussion, smart pointers are not a known Delphi idiom. I think that is a pity. But that would require a standard implementation (or a number of them) in the Delphi runtime library. Perhaps one day, we will see them there. Or they will be superseded by ARC. Who knows?

Rudy Velthuis

Thursday 31 August 2017

Implicitly imported units in a package.

Problem

Say, you write a package called MyControls.bpl. The units you put in it need a lot of units from TeeChart:

Package MyControls:

package MyControls;

...

contains
  MyUnit1, MyUnit2;

end.

Now, in MyUnit1 and myUnit2, you use the units TeeThis, TeeThat and TeeOther:

unit MyUnit1;

uses
  TeeThis, TeeThat;
unit MyUnit2;

uses
  TeeThat, TeeOther;

Then these three TeeXXX units will implicitly be imported (linked) into your package. So now your package contains MyUnit1, MyUnit2, TeeThis, TeeThat and TeeOther, although your contains section only explicitly mentions the first two, the ones you wrote.

Then, if a customer of yours installs your package, and then tries to install another package that needs these units too, the IDE will tell them to reference your package, because these units are installed already, and they can't be in any other package. That will happen even if your package knows nothing about the other, and the other knows nothing about yours. Because both need the same units.

Solution

Instead of implicitly using these units, reference the package that contains them:

package myControls;

...

contains
  MyUnit1, MyUnit2;

requires
  TeeChart;

end.

Now only your units will be in your package, and the units it needs will be used (referenced) from that other package. This way your package will (probably) be a lot smaller and other packages can use the TeeChart units in the same way.

Do not forget

Of course, now you will have to make sure that the user also has the TeeChart package. If you are allowed to distribute it (I guess so, but I am not a lawyer, so read the license), then you can do that and install it if the user doesn't have it installed yet.

Conclusion

Never, ever, ignore the message that certain units are implicitly imported into your package. Always make sure that this doesn't happen. Instead, reference a package that already contains them. Your packages will be smaller, and there will be no naming conflicts.

Tuesday 4 July 2017

Article "Addressing pointers" updated

A few years ago, in his Wiert Corner, Jeroen commented that some things were still missing from my article Addressing pointers.

It took me a long time, but now I got around to it and updated the article with info about old Turbo-Pascal-style objects and with procedural types, method types and anonymous methods.

Have "fun" reading.

Saturday 20 May 2017

New article on writing DLLS

Especially on StackOverflow, but also in the Embarcadero forums, I frequently encounter (stupidly) written DLLs, e.g. for hardware or some exotic piece of software that expose things like C++ classes or Delphi AnsiStrings or dynamic arrays. It should have been obvious that such DLLs can not be used by most users who use another language to communicate with the DLL.

That is why I wrote an article called "DLL dos and don'ts" about how I would write DLLs that can be consumed by (almost) every language. I think that it could be a good read for anyone writing DLLs, not just for Delphi users.

Sunday 1 January 2017

BigIntegers and BigDecimals now on GitHub

To make it easier for me, as well as for downloaders, I put my BigIntegers and BigDecimals code on GitHub.

You can find these and related files (tests, debug visualizers, test data generators, etc.) on my DelphiBigNumbers project on GitHub.