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 BigNumbers project on GitHub.

Monday, 18 July 2016

New: Velthuis.AutoConsole unit

I often (really, very often) write console programs. Most of them are simple test programs to test algorithms, calculations or language features, or stuff copied from StackOverflow.

But console programs, if run from the IDE, or from the Windows Explorer, have one annoying feature: they close immediately if they are finished. So if my program has many Writelns in it, they simply rush by and the console window closes before I can inspect the output. To avoid that, I often put Readln; in the last line of the program. If the program is finished, it pauses and waits for me to press Enter.

But such a Readln; is annoying if I really want to run the program from an existing console window (aka a Windows command line). Then I don't want to have to press Enter, because the output is shown in the current console window already, and that won't close until I tell it to.

To get the best of both worlds, I wrote my simple Velthuis.AutoConsole unit. Put it in the uses clause of your console program and, if the program is a console program and it was started from the command line, or if it is not a console program, it will do nothing. But if it is a console program and it was started any other way (from the Windows Explorer, from the Delphi IDE with or without debugger, from another non-console program), then, before the console window can close, it will display:

Press any key...

Then I really only have to press a key (it does not have to be Enter, it can be any key) and the program will end, closing the console Window.

I placed the new unit in the zip for my Console unit. You can download it from the page I linked to.

Here is the code of this simple unit. I think I may have to add a ConsoleCtrlHandler, so the window is not closed when the user presses Ctrl+C or closes the console window with the  x  close button on the window frame. But I am not sure if that is necessary. If I press those, I usually want to stop the program anyway. Comments on this are welcome.

unit Velthuis.AutoConsole;

interface

implementation

uses
  Velthuis.Console,    // as described in the link above 
  Winapi.Windows;

function GetConsoleWindow: HWnd; stdcall; 
  external 'kernel32.dll' name 'GetConsoleWindow';

function StartedFromConsole: Boolean;
var
  ConsoleHWnd: THandle;
  ProcessId: DWORD;
begin
  ConsoleHwnd := GetConsoleWindow;
  if ConsoleHWnd <> 0 then
  begin
    GetWindowThreadProcessId(ConsoleHWnd, ProcessId);
    Result := GetCurrentProcessId <> ProcessId;
  end
  else 
    Result := False;
end;

procedure Pause;
begin
  if IsConsole and not StartedFromConsole then
  begin
    Write('Press any key... ');
    ReadKey;
  end;
end;

initialization

finalization
  Pause;

end.

Update

I removed the reliance on the Velthuis.Console unit. I added this piece, a simplified version of ReadKey, which does not try to translate the key, making things a lot simpler:

procedure WaitForInput;
var
  InputRec: TInputRecord;
  NumRead: Cardinal;
  OldKeyMode: DWORD;
  StdIn: THandle;
begin
  StdIn := GetStdHandle(STD_INPUT_HANDLE);
  GetConsoleMode(StdIn, OldKeyMode);
  SetConsoleMode(StdIn, 0);
  repeat
    ReadConsoleInput(StdIn, InputRec, 1, NumRead);
  until (InputRec.EventType and KEY_EVENT <> 0) and InputRec.Event.KeyEvent.bKeyDown;
  SetConsoleMode(StdIn, OldKeyMode);
end;

and changed the Pause procedure a little:

procedure Pause;
begin
  if IsConsole and not StartedFromConsole then
  begin
    Write('Press any key... ');
    WaitForInput;
  end;
end;

Friday, 20 May 2016

New blogging site

The blog that I had on the TeamB site is not functional anymore. I guess that is because it was still hosted by Borland, as far as I know. My blog pages were moved to the new Embarcadero Community site, but all comments were lost (sorry folks, but I had nothing to do with this), my user name is listed as "Rudy Velthuis Velthuis (TeamB)" and there seems to be nothing I can do to change this. And a search for "Rudy" or "Velthuis" on the Bloggers page doesn't find it either, or well, it says it found one instance, but it doesn't show it.

But what is worse, I can't write, edit or administer the posts there. That is why I decided to reactivate this blog site, which I had reserved ages ago. I have complete control over what I post here. I managed to copy most posts from the Community site over, and updated the formatting where that was necessary.

So please, if you had any links to my old blog, please update them to point to this site. The old URL will redirect to the Community site, but I will probably not be using that anymore. I hope that it will be picked up by DelphiFeeds and similar sites soon, because I intend to write regular blog posts again.

Update

I was told that the glitches and problems with the moved blogs on the Community server are going to be solved, and that the site will be made faster as well. They'll even try to re-install the comments. If that is the case, I will move back to the Community site again. Hang on.