Einzelnen Beitrag anzeigen

Benmik

Registriert seit: 11. Apr 2009
542 Beiträge
 
Delphi 11 Alexandria
 
#17

AW: Aufrufen einer Unterprozedur mit AsyncCalls

  Alt 11. Jun 2016, 12:22
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:
  1. Jede Prozedur/Funktion hat eine Adresse
  2. Der Compiler kennt diese Adresse und hat sie irgendwo gespeichert
  3. 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?
  Mit Zitat antworten Zitat