Category Archives: Tips & tricks

Add an array property enumerator in FPC/Lazarus

I’ve been wishing object pascal supported array property enumeration for a long time:

  TTest = class
    // ...
    property Objects[Index: Integer]: TObject read GetObject;
  end;
var
  Test: TTest;
  Obj: TObject;
begin
  //...
  for Obj in Test.Objects do
    //...
end;

Unfortunately, this should not be possible because enumerators can be defined only with the GetEnumerator function in class/record/interface and other types through helper classes.

This is the case in Delphi.

I discovered a bug in FPC that allows to declare an enumerator for an array property. Studying the FPC code, I found out that it does not check if the array property parameters match the property definition. Instead it checks whether an (overloaded) getter function with the supplied parameter set exists. So you create a second parameterless getter function that returns the enumerator and, voila, your “for Obj in Test.Objects do” loop compiles!

  TTest = class
    // ...
    // usual getter
    function GetObject(Index: Integer): TObject; overload;
    // overloaded getter used for the enumerator
    function GetObject: TTestObjectEnumerator; overload;
    property Objects[Index: Integer]: TObject read GetObject;
  end;

TTestObjectEnumerator can be either a class or record – FPC frees enumerators automatically. But there is one thing you have to do in contrast to usual enumerators – you obviously have to define TTestObjectEnumerator.GetEnumerator that returns Self so that the compiler finds the GetEnumerator function (thanks Sven Barth for the idea).

What are the benefits of having such an array property enumerator? Clear and understandable code and also the ability to have enumerator for every array property within an object. As an example consider this code:

procedure TForm1.Check;
var
  Comp: TComponent;
begin
  for Comp in Self do
    // ...
end;

From my POV it’s quite unclear what collection is used for the enumerator. The default enumerator can even be overloaded with a helper, which can lead to unpredictable results.

With the array property enumerator, the code is much clearer and you can use more enumerators in the same object:

procedure TForm1.Check;
var
  Comp: TComponent;
  Cont: TControl;
begin
  for Comp in Components do
    // ...
  for Cont in Controls do
    // ...
end;

Download the example code PropArrayEnum.zip:
arraypropenum.lpr: enable property array enumerator in your own class.
tobjectsenum.lpr: enhance TStrings.Objects with an enumerator (with a helper class).

Further reading:
feature request report
fpc-devel thread 1
fpc-devel thread 2 (a), fpc-devel thread 2 (b)

Procedure call with delay in Delphi and Lazarus

With the latest threading library from Delphi XE7, it’s been quite easy to start a procedure with a delay. The TTask does its job very well:

  TTask.Create(
    procedure
    begin
      Sleep(1000);//delay of 1s
      ShowMessage('Hello');
    end).Start;

This is very easy but it has one big issue – you cannot easily stop the execution before the delay limit is reached and it can be used only in the latest Delphi versions. So I came up with my own alternative:

  TOCallWithDelay.Create(Self, 1000,
    procedure
    begin
      ShowMessage('Hello');
    end);

The first parameter is the component owner – if the owner is destroyed (e.g. you close the dialog before the limit), the procedure call won’t be executed. The class works in Delphi 5 and newer on all supported platforms, including mobile/ARC. FPC/Lazarus is supported as well.

If you want to stop the object in the future, use:

procedure TForm1.Btn1Click(Sender: TObject);
var
  xCall: TOCallWithDelay;
begin
  TOCallWithDelay.Create(Self, @xCall, 1000,
    procedure
    begin
      ShowMessage('Hello');
    end);

  xCall.Free;//stop TOCallWithDelay -> do not call the method
end;

Download the source code with a simple test project:
OCallWithDelay.zip