Nachdem ich mich ziemlich in das Problem verbissen habe, möchte ich hier eine Lösung präsentieren. Nochmal zu Erinnerung, hier geht es nicht um die Lösung eines konkreten Problems, da eine solche bereits vorliegt. Hier geht es um das Verstehen von Strukturen.
Ausgangspunkt war der apodiktische Verweis von David Heffernan auf die Dokumentation, die klar aussagt: "
Nested procedures and functions (routines declared within other routines) cannot be used as procedural values." Demgegenüber steht die Erfahrung, dass viele Dinge möglich sind, auch wenn die Dokumentation (und David Heffernan) etwas anderes sagen.
Ich hatte einfach folgende (laienhafte) Vorstellung:
- Jede Prozedur/Funktion hat eine Adresse
- Der Compiler kennt diese Adresse und hat sie irgendwo gespeichert
- Findet man diese Adresse, kann man die Prozedur/Funktion ausführen
Wie also kriegt man einen Pointer auf diese Adresse?
Erhellend (und zunächst enttäuschend) war
diese Diskussion auf Stackoverflow vor 4 Jahren, wieder mit Beteiligung von DH. Nach dem Verweis auf die Dokumentation führte er aus:"
If I recall correctly, an extra, hidden, parameter is passed to nested functions with the pointer to the enclosing stack frame. This is omitted in 32 bit code if no reference is made to the enclosing environment." Der Threadersteller schaute augenscheinlich im Assemblercode nach und fand: "
It's an implicit parameter alright! The compiler assumes it has its thing in 'rcx' and the parameters to the function are at 'rdx' and 'r8', while in fact there's no 'its thing' and the parameters are at 'rcx' and 'rdx'." Alles ein klein wenig außerhalb meiner Reichweite.
Aber dann gibt es noch
diesen Text mit dem Titel: "
How to pass nested routine as a procedural parameter (32 bit )". Verblüffend, wenn man die Aussage der Dokumentation bedenkt. Dieser Text führt zu folgendem Code (Voraussetzung: Die Änderung von Sir Rufo mit
reference to
):
Delphi-Quellcode:
procedure TForm1.Haupt;
var i:integer;
//-------------------------------------------------------
function TuWas(i:integer):integer;
begin
end;
//-------------------------------------------------------
procedure RufeAuf;
var p:Pointer;
begin
p := @TuWas;
TAsyncCalls.Invoke(
procedure
var i:integer;
begin
For i := 0 to 10 do
If i < 11
then AsyncHelper.AddTask(TAsyncCalls.Invoke<integer>(p,i));
end);
end;
//-------------------------------------------------------
begin
RufeAuf;
end;
Dieser Code funktioniert, sogar mit der Helper-Klasse von Garco Zajik. Jetzt ist p nicht direkt eine prozedurale Variable, ist aber eigentlich wurscht, die Frage war ja, ob die Klassenmethode in eine Unterprozedur verlagert werden kann. Wie es aussieht, geht das doch. Was sagen die Experten?