[fpc-pascal] Re: Delphi's anonymous functions in Free Pascal

Marco van de Voort marcov at stack.nl
Tue Oct 25 13:11:05 CEST 2011


In our previous episode, tcoq at free.fr said:
> > it will be more readable (imo)? The example still does not take
> 
> This seldom happens, but here I fully agree with Florian. ;-) Every
> example of anonymous methods I have seen so far, can easily be done with
> OP's procedure variables too.  Maybe anonymous methods were introduced
> in other languages because they didn't have something like OP's
> procedure variables. I guess Object Pascal was yet again ahead of
> everybody else. :)

I hinted on it in earlier procedures, but here a typical anonymous method
usecase.

You are in a thread and want to call a method or procedure in a different
thread. (to the mainthread or a different event driven thread)

BUT, the said function takes parameters. WDYD?

Classically, you create some record, add all paramters to it, dump it in
some queue (or argument to a windows message if you play it dirty), write a
procedure in the destination thread that gets the record from the queue, and
calls the function with the parameters.

One can avoid doing the queue for every case by using a bit of OOP, but
still you need to inherit and implement that object for every signature of a
method that you want to call cross-thread this way. Loading parameters into
it in the source thread, and calling the destination procedure with
parameters in the execute method for the destination thread.

Using the anonymous function however, you put the call within the anonymous
function block. This blocks captures all necessary state, and boom it is
away.

Visually:


Problem: 

call

targetobject.destinationfunction(a:integer;b:someobject;c:string);

in a different thread.


Classic solution:

type  // for every signature:
   TMyobject = class(TpseudoClosure)
                a:integer;
                b:someobject;
                c:string;
                tgob: TTargetObject;
                procedure execute; override;
              end;

procedure TMyObject.Execute;

begin
  tgob.destinationfunction(a,b,c);
end;   

// once:

var
  thrqueue :TSomeGenericThreadsafeQUeue<TPseudoClosure>;

procedure tmythread.syncfunc;

var x:TPseudoClosure;
begin
  x:=getqueuecurrentthread().popitem; // get closure object from queue
  x.execute;
  x.free;
end;

// calling from source thread;

var x : TPseudoclosure

x:=TMyObject.Create;
x.a:=a;
x.b:=b;
x.c:=c;
tboj:=targetobject;
queuefortargetthread.pushitem(x);
queuefortargetthread.queue(syncfunc); // call item async in other thread.
                                      // tthread.queue not yet in FPC

I have many such constructs in my current framework. Of course you start to
double use simple objects, and not all cases use 3 parameters. It is just
for the idea.

Note that tabstractclosure, the generic queue etc must also be defined, but that is
so general it could be in the RTL, and not a burden

---------------------------------
vs
-~-------------------------------

Equivalent solution with anon functions:

targethread.queue(
   procedure(targetobject:ttargetobject;a:integer;b:someobject;c:string)
              begin
                targetobject.destinationprocedure(a,b,c);
              end;

Note how common this looks compared to the original.


Btw: I don't know this function this well. I only described it how I
saw it in several D2010 blogs. I still support D2009, and the framework
design was even older, so I don't actively use it.




More information about the fpc-pascal mailing list