Friday, 9 March 2018

Converting line endings

eolconv

An up-to-date version of this and other files can be found in my CommandLineTools project on GitHub.

On Stack Overflow and in the Embarcadero forums I regularly see people ask how to convert a file with Unix line endings to Windows line endings, or vice versa. The usual advice is to load the file into Notepad++ and to save it with the desired line ending format.

But Delphi can do this too, either using AdjustLineBreaks or using a TStringList. That is why I wrote this simple command line tool. The actual conversion is extremely simple:

  • Load the file into a TStringList.
  • Set the LineBreak property of the string list, depending on the desired output format (Windows or Unix).
  • Convert the file depending on the encoding: 8 bit, 16 bit little endian or 16 bit big endian.
  • Rename the old file to a name with a unique extension.
  • Rename the new file to the name of the old file.
  • Save the contents of the TStringList back to the file.

Most of the code is there to handle command line parameters.

Update

The TStringList way is simple to write, but is not only pretty slow and — if the file is large — can also require a lot of memory. The new way uses a buffer of up to 2MB to load blocks of the file and convert on the fly. This turned out to be a lot faster for large files.

Synopsis

EOLCONV [options ...] Name[s]

Multiple file names can be specified. EOLConv does not understand wildcards (yet), but for instance on the Windows command line, you can do something like:

for %f in (*.txt) do eolconv %f -U

That will convert all files with the extension .txt in the current directory to the Unix format.

Each original file will be preserved by renaming it, adding a unique .~1, .~2, etc. extension to the original file name. EOLConv will try to add extensions up to .~200, and if the file name with that extension exists too, EOLConv will give up and the original file will remain as it was.

Options

On Windows, all options start with either a / or a . On other platforms, they start with a .

Options are case insensitive.

If no options are specified, the files will be converted to the default line ending format for the platform. To set the line ending format to a specific style, use:

  • -W     to set the line endings to Windows format (CR+LF)*
  • -U     to set the line endings to Unix format (LF)*

* CR stands for Carriage Return (#13 in Delphi), LF stands for Line Feed (#10 in Delphi).

To get help, either start eolconv without any parameters, or specify -H or -?.

Finally

EOLConv is freeware. All rights are reserved. Its code is provided as is, expressly without a warranty of any kind. You use it at your own risk.

I hope this code is useful to you. If you use some of it, please credit me. If you modify or improve EOLConv, please send me the modifications.

I may improve or enhance EOLConv myself, and I will try to post changes here. But this is not a promise. Please don’t request features.

Rudy Velthuis

Sunday, 4 March 2018

AutoSuffix

I guess everyone who had to maintain one package for several different versions of Delphi had wished this was possible to do with a single package source. The alternative is to have different set of .dpk and .dproj files, each with a different name or suffix, for each version you want to cover.

Some, including me, devised a "clever" include file that set a different {$LIBSUFFIX } for each version. So did I, but every simple change to the package would remove the {$INCLUDE 'LibSuffixes.inc'} I put in the .dpk file and replace it with something like {$LIBSUFFIX '230'} if 230 was the suffix found in the .dproj file.

If you are very careful not to modifiy the package, and the include file is not removed, this compiles as intended, i.e. a file MyPackage.dpk compiles as MyPackage250.bpl, but if you try to install that way the IDE is not aware of the changed suffix, so it complains that it could not install MyPackage.bpl.

Note: The {$LIBSUFFIX} directive works for DLLs too, but there is no entry in the Project → Options... → Description page (there is not even a Description page). The previously mentioned "clever" includes work very well for DLLs (and so for DLL-based experts too). The IDE does not meddle with the contents of a .dpr file for a library.

This problem has been discussed many times on forums and in communities. On Stack Overflow, someone called LaKraven set out to create an IDE expert (AutoSuffix) to solve this problem. But I can't find this expert anymore. All the links I found to it are dead.

The New AutoSuffix Expert

So I wrote a simple expert myself and called it AutoSuffix too.

If the current project is a package, it adds a menu item to the context menu of the Project Manager with the title Add Version Suffix, under the Rename menu item. If you click it, it sets the suffix for the package to a number that matches the suffixes used for the version of the compiler, i.e. for Delphi 10.2 Tokyo, it sets it to 250, for 10.1 Berlin to 240, for XE3 to 170, etc.

I could have made this an expert that sets the suffix automatically, but I wanted to leave the user (myself too) a choice, so it must be done manually, using the context menu.

Tested

I only tested this in Delphi XE3, Delphi 10 Seattle, 10.1 Berlin and 10.2 Tokyo, but it probably works in versions inbetween as well. I will update this as soon as I have tested it more.

Installation

Get all the files from GitHub (either clone the repository or download the files as zip) and put them in a single directory. Open the package AutoSuffix.dproj in your IDE, select the project in the Project Manager and click the Install menu item from the context menu. If all is well, this installs the expert and adds one menu item to the context menu of the Project Manager: Add Version Suffix, under the Rename item. This menu item only appears if the project is a package.

Rudy Velthuis

Thursday, 1 February 2018

Optimizing ARC the hard way?

The problem

In a recent blog post, Dalija Prasnikar talks about how hard it would be to optimize ARC code, because most ARC object references will be passed by value, but not as const. Passing as const would eliminate the need for an implicit __ObjAddRef() call each time the object is passed (and an __ObjRelease() call when the routine with the parameter ends).

OK, ARC (automatic reference counting) has been used for many types already, most of all strings, dynamic arrays and interfaces. Code passing strings and dynarrays is generally optimized already, by specifying such parameters as const. There are probably only a few exceptions. It is also customary to do it with interfaces, and the few exceptions probably don't make a big difference in performance. But it is far from customary to do this for objects. Until ARC, it didn't matter if you passed an object as const or not. But these days, in ARC, it can make a big difference. And objects are passed around a lot, e.g. the Sender parameter of events, etc.

Note that most runtime and FMX code doesn't use const to pass objects either, nor do any of the third parties. Dalija notes that it is not hard to add const to each object parameter, but that it would break huge amounts of code, not only Embarcadero's, but everyone's.

Switch?

I read that and have been thinking about it a little. I first thought of introducing a switch (sound familiar?) in the new ARC compilers, that would make const optional and reduce compiler complaints about interface changes, to make this transition easier. But that would still require a huge amount of code changes, even if it could perhaps be done at a slower pace.

Const by default?

But then I thought of something else. If an object is passed as call-by-value, to a method or function, inside that method or function, you very seldom change the reference by assigning a new object to it. In other words, inside such a method or function, you hardly ever do something like:

    ObjectParam.Free; // necessary in non-ARC, optional in ARC
    ObjectParam := TSomeObject.Create;

Yes, you often change the state of the object by setting properties or by calling methods, but that can be done on a const reference as well. Const means you can't change the reference, not that you can't change the state of the object it refers to.

For almost all practical purposes, such objects passed can be treated as if they were passed as const already. So it would perhaps make sense, in a new version of the ARC compilers, to treat every pass-by-value as const. This would make the code compatible with the non-ARC compilers, while still avoiding a truckload of reference counting calls. This would probably optimize ARC code quite a lot. Of course code already using const would be compatible too. And it would not apply to code using var or out, only to plain pass-by-value object reference parameters.

And the very few methods that do actually re-use a parameter like above should simply be rewritten to use a (new) local variable for the new object. I doubt there is lot of code that does this anyway, so this change would not break a lot of code at all.

So making all passed-by-value objects const by default would probably break very little code, and optimize a lot of ARC code. It would not affect non-ARC code.

As for the old compilers: they would remain non-optimized, but their code would not have to be changed. A simple recompile (and the occasional change) would suffice.

I'd love to hear about your thoughts.

Monday, 22 January 2018

Accessing private methods of another class

In versions of Delphi before 10.1 Berlin, it was possible to access private members and private methods of a class simply by defining a class helper for it and accessing these private items in its methods.

But that it was so easy to access private members undermined the basic OO principles of information hiding and encapsulation. That is why this loophole (which could be considered a bug) was closed in Delphi 10.1 Berlin. But there was a lot of howling about this, because it was so easy and so convenient to have access to everything, even to other classes' "private parts".

There has been a lot of discussing going on about this. I will not repeat the arguments made there, but will state that I agree with the closing of the loophole. Accessing private members or methods is a hack, and a hack should never be easy. This hack was far too easy. Instead of using the hack, people should rather have tried to find other ways to achieve their goals.

But there is one argument of the pro-helper people that actually made sense: sometimes a class is not designed well, and you must resort to hacks.

Three hacks

They have all been mentioned before, in several blogs and StackOverflow answers, but I want to recap them here, in one post. All of these still require helper classes, as only there, you can still access private methods or members.

Accessing private methods

Say we have a class, let's call it THasPrivateMethods, that has at least one private method, let's say procedure PrivateMethod, that you want to call. The following three ways to call the private method have all been verified in Delphi 10.2.2 Tokyo. They should also work in previous versions.

Using assembler

The first way to get at private methods is to define a helper class with a new method. Here, I'll call the helper THelper and the first method CallingPrivateMethodUsingAsm. This method must be an assembler method:

type
  THelper = class helper for THasPrivateMethods
  public
    procedure CallingPrivateMethodUsingAsm;
  end;

The implementation is extremely simple:

procedure THelper.CallingPrivateMethodUsingAsm;
asm
       JMP     THasPrivateMethods.PrivateMethod
end;

In other words, in assembler, helper methods can still access private methods.

Using a method pointer

For those who are not very good with assembler, there is another, more tedious way, using a method pointer:

    procedure CallingPrivateMethodUsingTMethod; inline;

The implementation takes the address of the private method (which can apparently still be queried), puts it in a method pointer and then calls the method pointer:

type
  TPrivateMethod = procedure of object;

...
  
procedure THelper.CallingPrivateMethodUsingTMethod;
var
  M: TMethod;
begin
  M.Code := @THasPrivateMethods.PrivateMethod;
  M.Data := Self;
  TPrivateMethod(M);
end;

Using with

Inside a method of a class helper, you can't call a private method directly anymore, but you can use a simple trick (probably forgotten when they closed the loophole), using "with Self do". I am no fan of "with", but in this special case, it works as expected:

procedure THelper.CallingPrivateMethodUsingWith;
begin
  with Self do PrivateMethod;
end;

Yes, it is that easy. But you must use "with Self do" to call the private method. Now, if you inline this method, there is absolutely no overhead. It will work as if you had called the private method in your own routine:

procedure NeedsAccessToPrivateMethod;
var
  H: THasPrivateMethods;
begin
  H := THasPrivateMethods.Create;
  try
    // ...
    H.CallingPrivateMethodUsingWith;
    // ...
  finally
    H.Free;
  end;
 end;
 

Again, if CallingPrivateMethodUsingWith is inlined, the resulting code is the same as if you had written:

  try
    // ...
    H.PrivateMethod;
    // ...
  finally

If you can read assembler, you can verify this in the CPU view of the IDE.

I will discuss accessing private fields in a later post.

Monday, 15 January 2018

Strings too slow outside WIN32?

In a recent debate I had it was said that strings in the Win64 runtime are too slow to be useful. That is, in my opinion, a gross exaggeration. It is true that the Win32 runtime library (RTL) has benefited a lot from the work of the FastCode project, usually with routines in extremely clever assembler. For all other platforms, often the routines are in plain Object Pascal, so no assembler is being used. Also, far fewer routines have been replaced by clever implementations.

One very obvious example of this is the Pos function, which searches if a certain string (I call that the Needle) can be found in a larger one (the Haystack). The Win32 implementation is in highly optimized assembler, written by Aleksandr Sharahov from the FastCode project, and licensed by CodeGear. The Win64 implementation is in plain Pascal (PUREPASCAL). But the implementation for UnicodeString is not the same, or even similar, to the implementation for AnsiString!

The implementation for UnicodeString is slower than the same routine for Win32. On my system a search in Win64 takes approx. 1.8 × the time it needs in Win32. On Win32, Pos for AnsiString is about as fast (or sometimes even slightly faster than) Pos for UnicodeString. But on Win64, Pos for AnsiString takes 2 × the time Pos for UnicodeString needs!

If you look at the sources in System.pas, you'll see that the Unicode version is slightly better optimized (searching for the first Char in the Needle first, and only checking the rest if a match was found).

For fun, I took the code for the UnicodeString implementation and converted it to work for AnsiString. It was slightly faster than System.Pos for UnicodeString, instead of 2 times as slow. I wonder why, in System.pas, the AnsiString implementation does not simply use the same code as that for UnicodeString, like I did. If I were a suspicious person, I would think it was done on purpose, to deprecate AnsiString by making it less usable.

But even that can be improved upon. I wrote three implementations of my own routine, one for AnsiString, one for UnicodeString and one for TBytes (many people have complained that TBytes lacks something like Pos and that was the reason they maintained the incredibly bad habit of using strings to store binary data — <shudder> — I wanted to take away that silly argument).

Code

Here is the code for my RVPosExA function (for what it's worth: these days, there is no difference between PosEx and Pos anymore: both have the exact same functionality and signature):

function RVPosExA(const Needle, Haystack: AnsiString; 
  Offset: Integer = 1): Integer;
type
  PUInt32 = ^UInt32;
  PUInt16 = ^UInt16;
{$IFNDEF CPU32BITS}
var
  LNeedleTip: UInt32;
  PNeedle: PAnsiChar;
  PHaystack, PEnd: PAnsiChar;
  LLenNeedle: Integer;
  LCmpMemOffset: Integer;
{$ENDIF}
begin
{$IFDEF CPU32BITS}
  // FastCode (asm) implementation.
  Result := System.Pos(Needle, Haystack, Offset); 
{$ELSE}
  if Offset - 1 + Length(Needle) > Length(Haystack) then
    Exit(0);
  Result := 0;
  PHaystack := PAnsiChar(Haystack) + Offset - 1;
  PEnd := PHaystack + Length(Haystack) - Length(Needle) + 1;
  case Length(Needle) of
    0: Exit(0);
    1:
      begin
        LNeedleTip := PByte(Needle)^;
        while PHaystack < PEnd do
          if PByte(PHaystack)^ = LNeedleTip then
            Exit(PHaystack - PAnsiChar(Haystack) + 1)
          else
            Inc(PHaystack);
        Exit(0);
      end;
    2:
      begin
        LNeedleTip := PUInt16(Needle)^;
        while PHaystack < PEnd do
          if PUInt16(Haystack)^ = LNeedleTip then
            Exit(PHayStack - PAnsiChar(Haystack) + 1)
          else
            Inc(PHaystack);
        Exit(0);
      end;
    3:
      begin
        LNeedleTip := PUInt32(Needle)^; // if Needle is length 3, then top byte
                                        // is the #0 terminator
        while PHaystack < PEnd do
          if ((PUInt32(Haystack)^ xor LNeedleTip) and $FFFFFF) = 0 then
            Exit(PHaystack - PAnsiChar(Haystack) + 1)
          else
            Inc(PHaystack);
        Exit(0);
      end;
    4:
      begin
        LNeedleTip := PUInt32(Needle)^;
        while PHaystack < PEnd do
          if PUInt32(Haystack)^ = LNeedleTip then
            Exit(PHaystack - PAnsiChar(Haystack) + 1)
          else
            Inc(PHaystack);
        Exit(0);
      end;
    else
      begin
        LCmpMemOffset := SizeOf(UInt32) div SizeOf(AnsiChar);
        PNeedle := PAnsiChar(Needle) + LCmpMemOffset;
        LLenNeedle := Length(Needle) - LCmpMemOffset;
        LNeedleTip := PUInt32(Needle)^;
        while PHaystack < PEnd do
          if (PUInt32(PHaystack)^ = LNeedleTip) and 
              CompareMem(PHaystack + LCmpMemOffset, PNeedle, LLenNeedle) then
            Exit(PHaystack - PAnsiChar(Haystack) + 1)
          else
            Inc(PHaystack);
      end;
  end;
{$ENDIF}
end;

As you can see, under Win32, it simply jumps to System.Pos, as that is the fastest anyway. But on all other platforms, it searches the Haystack 4-byte-wise (if the Needle is larger than 4 elements), and if it found something, then it searches the rest using CompareMem.

Timing

Here is a slightly reformatted output of a test program (I put the WIN32 and the WIN64 columns beside each other, to save space):

Different versions of Pos(Needle, Haystack: <sometype>; Offset: Integer): Integer
where <sometype> is UnicodeString, AnsiString or TBytes

Testing with Haystack lengths of 50, 200, 3000, 4000 and 300000
and Needle lengths of 1, 3, 8 and 20
5 * 4 * 2000 = 40000 loops

WIN64                                   WIN32

UnicodeString                           UnicodeString
-------------                           -------------
System.Pos:           2428 ms           System.Pos:           1051 ms
StrUtils.PosEx:       2258 ms           StrUtils.PosEx:       1070 ms
RVPosExU:             1071 ms           RVPosExU:             1050 ms

AnsiString                              AnsiString
----------                              ----------
System.Pos:           4956 ms           System.Pos:           1046 ms
AnsiStrings.PosEx:    4959 ms           AnsiStrings.PosEx:    1051 ms
OrgPosA:              5129 ms           OrgPosA:              5712 ms
PosUModForA:          1958 ms           PosUModForA:          3744 ms
RVPosExA:             1322 ms           RVPosExA:             1086 ms

TBytes                                  TBytes
------                                  ------
RVPosEXB:              998 ms           RVPosEXB:             2754 ms


Haystack: random string of 500000000 ASCII characters or bytes
Needle: last 10 characters of Haystack = 'WRDURJVDFA'

WIN64                                   WIN32

UnicodeString                           UnicodeString
-------------                           -------------
System.Pos:            847 ms           System.Pos:            421 ms
Strutils.PosEx:        827 ms           Strutils.PosEx:        414 ms
RVPosExU:              421 ms           RVPosExU:              438 ms

AnsiString                              AnsiString
----------                              ----------
System.Pos:           1735 ms           System.Pos:            428 ms
AnsiStrings.PosEx:    1831 ms           AnsiStrings.PosEx:     428 ms
OrgPosA:              1749 ms           OrgPosA:              2687 ms
PosUModForA:           708 ms           PosUModForA:          1525 ms
RVPosExA:              368 ms           RVPosExA:              423 ms
RvPosExA(,,Offset):    200 ms           RvPosExA(,,Offset):    220 ms

TBytes                                  TBytes
------                                  ------
RVPosExB(TBytes):      385 ms           RVPosExB(TBytes):     1095 ms

The routines RVPosExA, RVPosExU and RVPosExB are my implementations for AnsiString, UnicodeString and TBytes respectively. OrgPosA is the original code for Pos for AnsiString, while PosUModForA is the original PUREPASCAL code for Pos for UnicodeString, modified for AnsiString.

As you can see, the PosUModForA routine is almost twice as fast as the rather braindead OrgPosA, and in WIN32, the RVPosEx<A/U/B> implementations are faster than the others.

I didn't check, but it is well possible that one of the plain Pascal versions of the FastCode project is faster. But for me, this implementation is a start and proof, that with a few simple optimizations string routines could be made faster. Perhaps, one day, Embarcadero will adopt more of the plain Pascal code from the FastCode project.

The code for the routines and the program that produces the output above can be downloaded from my website.

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.