[fpc-pascal] Delphi compatible anonymous functions

Craig Peterson craig at scootersoftware.com
Fri May 16 17:45:43 CEST 2014


On 5/16/2014 9:13 AM, Dmitry Boyarintsev wrote:
> Just a suggestion. Try to rewrite the code not to use anonymous functions.
> The need for their support in FPC will go away.

The code in question doesn't use anonymous methods yet, but I've already
tried the alternatives and they are not an improvement.  I don't really
want to re-open the debate about their merits and syntax, but I can
explain why I need them:

1) I want to use the OmniThreadLibrary.  Setting up objects and thread
descendents is fine in moderation, but it's too much of a hassle when
you just want a quick parallel for loop.

2) Our application has multiple independent top-level windows, like a
web browser.  There is no "MainForm".  Each window can show modal
dialogs that only disable that specific window.

In a traditional Delphi/Lazarus app, showing a modal dialog looks
something like this:

  procedure TMyForm.ShowMyDialog;
  begin
    Dlg := TMyDialog.Create(...)
    try
      if Dlg.ShowModal = mrOk then
        Do something
    finally
      Dlg.Free
    end;
  end;

To make that work without blocking the rest of the application that
needs to be split into two functions:

  procedure TMyForm.ShowMyDialog;
  begin
    Dlg := TMyDialog.Create(...);
    Dlg.ShowModalNonBlocking(ShowMyDialogDone);
  end;

  procedure TMyForm.ShowMyDialogDone(ADialog: TForm);
  begin
    if ADialog.ModalResult = mrOk then
      Do something
    ADialog.Free
  end;

It's unwieldy with the simplest of dialogs, and gets much worse if you
need to share state between the "Show" and "Done" functions, or if the
dialog is only shown selectively.

To solve that, years ago we started using cooperative threads
(coroutines/fibers) in our GUI thread.  I re-implemented ShowModal so it
handles the details internally.  When you call it it displays the
dialog, just like the ShowModalNonBlocking() call would, but then
switches to another "thread" that processes messages.  When the dialog
is finally closed the running thread switches back to the ShowModal
call, which then exits like normal.  As a result, even though we can
have several independent modal dialogs up at once, the code is all
identical to the first example.  Everything is nicely encapsulated and
sharing state before and after the dialog is trivial.

Unfortunately, fibers introduce incompatibilities on both Windows and OS
X, and are deprecated on OS X, so we need to stop using them.  They are
by far the cleanest approach, and I'd keep using them if I had any
choice whatsoever.

The second best alternative is to use closures and anonymous methods.
The syntax isn't as clean, but at least everything is kept within a
single "ShowMyDialog" call.  In that case it would become something like:

  procedure TMyForm.ShowMyDialog;
  begin
   Dlg := TMyDialog.Create(...);
   Dlg.ShowModal(
     procedure
     begin
       if Dlg.ModalResult = mrOk then
         Do something
       Dlg.Free;
     end;
  end;

Again, these examples are extremely simplified.  The benefits are much
greater when you need to use variables in the outer scope.  In our
application we have over 90 dialogs and almost 400 calls to ShowModal,
so the easier I can make that the better.

-- 
Craig Peterson
Scooter Software




More information about the fpc-pascal mailing list