[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