Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Problem mit Record-Parametern an MethodenPointer bzw. FunktionsPointer (https://www.delphipraxis.net/208157-problem-mit-record-parametern-methodenpointer-bzw-funktionspointer.html)

Andreas13 19. Jun 2021 22:34

Delphi-Version: 10.3 Rio

Problem mit Record-Parametern an MethodenPointer bzw. FunktionsPointer
 
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo Community,

ich stehe auf dem Schlauch und bräuchte mal wieder Eure Hilfe: Es geht um den Aufruf von Methoden und Funktionen über MethodenPointer und FunktionsPointer, wobei die Pointer einen Record als Parameter mitbekommen. Hierbei habe ich 3 verschiedene Varianten:

1: MethodenPointer direkt verwenden
2: Funktion wird über den FunktionsPointer aufgerufen
3: Funktion wird über den MethodenPointer aufgerufen

Um die Ergebnisse auseinanderzuhalten, machen die Routinen Folgendes:

– Methode: Bildet Eingabe hoch 3
– Funktion: Gibt das Quadrat der Eingabe aus

Alle drei Fälle liefern zwar das korrekte Ergebnis, allerdings ist im Fall 3 innerhalb der Funktion der Inhalt des Parameter-Records ein Zufallswert, trotzdem kommt das korrekte Resultat heraus. Das verwirrt mich etwas.

Code:
Di Ergebnisse:

1: Methode wird direkt aufgerufen:
MyMethode wurde aufgerufen!
Rec.X[0] = 2.00000000000000E+0000
Rec.X[1] = 3.00000000000000E+0000
Y = X^3 = 8 + 27 -----> korrekt!


2: Funktion wird über den FunktionsPointer aufgerufen:
MyFunktion wurde aufgerufen!
Rec.X[0] = 2.00000000000000E+0000
Rec.X[1] = 3.00000000000000E+0000
Y = X^2 = 4 + 9 -----> korrekt!


3: Funktion wird über den MethodenPointer aufgerufen:
MyFunktion wurde aufgerufen!
Rec.X[0] = 2.13089722546296E-0317 -----> FALSCHER Parameter-Inhalt
Rec.X[1] = 4.24412110025147E-0314 -----> FALSCHER Parameter-Inhalt
Y = X^2 = 4 + 9 -----> trotzdem korrekt! ???
Im Anhang habe ich ein einfaches Konsolenprogramm als Demo dazu.
Vielleicht kann mir jemand auf die Sprünge helfen, denn anscheinend sehe ich vor lauter Bäumen den Wald nicht mehr…

Danke für Eure Hilfe im Voraus!

Gruß, Andreas

himitsu 20. Jun 2021 02:31

AW: Problem mit Record-Parametern an MethodenPointer bzw. FunktionsPointer
 
Irgendwer (du) quetscht grob fahrlässig einen Funktions-Zeiger in eine Methoden-Zeiger-Variable,
wobei natürlich die Signatur überhaupt nicht kompatibel sind.

Wenn sich jetzt jemand wundert, dass die übergebenen Parameter nicht stimmen, dann ist er selbst Schuld,
weil er den impliziten (unsichtbaren) ersten Parameter "Self" ignoriert hat.

Delphi-Quellcode:
Function MyBösartigeFalscheMethodenFunktion(Self: TObject; CONST Rec: DoubleDouble): DoubleDouble;




Und nun rate mal, wie TMethod.Data in die Funktion Methode rein kommt.





Zitat:

trotzdem korrekt! ???
Ja klar, weil muß ja korrekt sein,
da Anzeige-Werte <> Rückgabe-Werte. :roll:

Zitat:

Delphi-Quellcode:
  WriteLn('Rec.X[0] = ', Rec.X[0]);
  WriteLn('Rec.X[1] = ', Rec.X[1]);

  ClearRec(Result); <<<<<<<<<<<<<<<<<<<<<<<<<

  Result.X[0]:= Rec.X[0]*Rec.X[0];
  Result.X[1]:= Rec.X[1]*Rec.X[1];
End;{MyFunktion}


Andreas13 20. Jun 2021 11:36

AW: Problem mit Record-Parametern an MethodenPointer bzw. FunktionsPointer
 
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo Himitsu,
danke für Deine Anregungen und Hilfe. Ich möchte mein Problem etwas verdeutlichen: In Jahrzehnte-langer Arbeit habe ich eine große Menge an mathematischen Funktionen und Prozeduren zur Lösung verschiedenster Probleme erstellt. Meine Bibliothek liegt in proceduraler Form vor, also ohne Klassen und Objekte. Die unterschiedlichen Aufgaben (hier vereinfacht durch „MyFunktion“) übergebe ich an die Routinen mittels ProzedurPointer, daher haben all die Routinen zum Empfangen der konkretisierten Aufgabe entsprechende Pointer. Zuerst hatte ich einfache ProzedurPointer / FunktionsPointer verwendet.

Da ich in den konkreten Anwendungen jedoch auch eigene Klassen und Objekte benutze, habe ich vor gut 15 Jahren alle ProzedurPointer in MethodenPointer geändert. Dann konnte allerdings ich die Bibliotheks-Routinen von einfachen Prozeduren/Funktionen nicht mehr aufrufen.
Zum Glück fand ich in https://www.delphi-treff.de/object-p...ethodenzeiger/ die Möglichkeit, „eine gewöhnliche Prozedur als Methode zu missbrauchen“. Und das hat bisher auch hervorragend funktioniert. Und damit kann ich mit MethodenPointern sowohl Methoden, wie auch externe Funktionen aufrufen.

Seit kurzem versuche ich einige meiner bisherigen Berechnungen(Genauigkeit = Extended bzw. Multipräzisions-Arithmetik) auf den DatenType DoubleDouble bzw. QuadDouble der Neslib.MultiPrecision-Bibliothek: https://github.com/neslib/Neslib.MultiPrecision umzustellen.
DoubleDouble entspricht formal obigem Record.

Und hierbei tritt das neue Problem auf, dass der Record DoubleDouble – im Gegensatz zum einfachen Extended oder Double – nicht mehr als Argument an in FunktionsPointer gecastete MethodenPointer übergeben wird. Es wird zwar die richtige Funktion aufgerufen, aber der Inhalt des Records ist zufällig, als hätte der Compiler eine neue Variable auf dem Stack abgelegt und „vergessen“, den Inhalt des Parameter-Records reinzukopieren. Auch eine Änderung des Parameters von CONST in VAR, oder ohne beides, bringt nichts.

Du hast recht, das frühere Ergebnis war nicht korrekt, obwohl ich die Löschroutine ClearRec(..) genau aus diesem Grund eingebaut hatte, um nicht korrekte Ergebnisse der Vorrunde ungewollt zu verschleppen und als korrekt zu übernehmen.

Nach Beheben einiger Fehlern von mir & Hinzufügen eines Pointers in der Klasse habe ich eine erweiterte Demo mit 4 Möglichkeiten gemacht, wobei ich die ersten beiden funktionierenden Fälle auskommentiert habe.
Leider wird immer noch ein Record mit Zufallsinhalt an die aufgerufene Funktion übergeben.

Was mache ich falsch? Und vor allem, wie wäre es richtig?
Dank & Gruß, Andreas

TiGü 21. Jun 2021 11:23

AW: Problem mit Record-Parametern an MethodenPointer bzw. FunktionsPointer
 
Es ist zu warm um groß Erklärung zu suchen und aufzuschreiben, aber ich habe hier einen Workaround für dich (Ausschnitt auf das Wesentliche zum Kopieren):

Delphi-Quellcode:
Function MyFunktion(CONST Rec: DoubleDouble): DoubleDouble;
// Hoch 2: --> X*X;
Begin
  WriteLn('MyFunktion wurde aufgerufen!');

  WriteLn('Rec.X[0] = ', Rec.X[0]);
  WriteLn('Rec.X[1] = ', Rec.X[1]);

  ClearRec(Result);

  Result.X[0]:= Rec.X[0]*Rec.X[0];
  Result.X[1]:= Rec.X[1]*Rec.X[1];
End;{MyFunktion}
{--------------}

function MyFunktionEx(Self: Pointer; const Rec: DoubleDouble): DoubleDouble;
Begin
  WriteLn('MyFunktionEx wurde aufgerufen!');
  Result := MyFunktion(Rec);
End;

VAR
  A   : TTestObject;
  MPtr : Tf_x_Methode; // MethodenPointer
  FuPtr: Tf_x_Function; // FunktionsPointer

  X, Y: DoubleDouble;


Begin
  Try
    A:= TTestObject.Create;
    Try
       X.X[0]:= 2;
       X.X[1]:= 3;

       ClearRec(Y);

       WriteLn;

      // 3: EXTERNEN MethodenPointer als FunktionsPointer verwenden:
      WriteLn('3: Funktion wird über den EXTERNEN MethodenPointer aufgerufen:');
      // A:= TTestObject.Create; ist nötig!!!!

     // EXTERNEN MethodenPointer in FunktionsPointer umwandeln:
      TMethod(MPtr).Code:= @MyFunktionEx; // wird vom Compiler NICHT gesetzt, weil EXTERNER MethodenPointer!
      TMethod(MPtr).Data:= @A; // selber setzen

      ClearRec(Y);
      Y:= MPtr(X); // Ein Record mit Zufallsinhalt wird an die Funktion übergeben!
      Print('Y = X^2 = ', Y);

      WriteLn;
      WriteLn;

      WriteLn('4: Funktion wird über den MethodenPointer des Objektes aufgerufen:');

      TMethod(A.Ptr).Code:= @MyFunktionEx; // TMethod(Ptr).Data wird vom Compiler NICHT automatisch gesetzt!
      TMethod(A.Ptr).Data:= @A; // Sonst ist Record X ist in der Funktion gar NICHT existent!

      ClearRec(Y);
      Y:= A.Ptr(X);  // Ein Record mit Zufallsinhalt wird an die Funktion übergeben!
      Print('Y = X^2 = ', Y);
Code:
3: Funktion wird über den EXTERNEN MethodenPointer aufgerufen:
MyFunktionEx wurde aufgerufen!
MyFunktion wurde aufgerufen!
Rec.X[0] = 2.00000000000000E+0000
Rec.X[1] = 3.00000000000000E+0000
Y = X^2 = 4 + 9


4: Funktion wird über den MethodenPointer des Objektes aufgerufen:
MyFunktionEx wurde aufgerufen!
MyFunktion wurde aufgerufen!
Rec.X[0] = 2.00000000000000E+0000
Rec.X[1] = 3.00000000000000E+0000
Y = X^2 = 4 + 9

Andreas13 21. Jun 2021 13:43

AW: Problem mit Record-Parametern an MethodenPointer bzw. FunktionsPointer
 
Vielen Dank, TiGü: Es funktioniert!
Nach Deiner Idee konnte ich den Aufruf sogar noch weiter vereinfachen, indem ich direkt MyFunktion(..) um den Eintrag
Delphi-Quellcode:
Self: Pointer;
erweitert habe:
Delphi-Quellcode:
Function MyFunktion(Self: Pointer; CONST Rec: DoubleDouble): DoubleDouble;
. . .
Zwar kapiere ich immer noch nicht, warum der mit einfachen Datentypen gängige Weg mit Records nur mit Self funktioniert, aber im Moment ist für mich nur wichtig, daß es klappt. Ich muß ja nicht alles verstehen...

Danke Dir noch einmal: Du hast mir sehr geholfen! :thumb: :angel:
Gruß, Andreas
PS:
So funktionieren alle vier Varianten!

Andreas13 21. Jun 2021 16:00

AW: Problem mit Record-Parametern an MethodenPointer bzw. FunktionsPointer
 
Sorry, ich war wohl etwas voreilig und muß meine letzte pauschale Aussage etwas relativieren: :oops: Variante 2, also die direkte Verwendung des FunktionsPointers funktioniert doch NICHT (mehr).

Abhilfe: Stets MethodenPointer benutzen! Ein auf FunktionsPointer gecasteter externer MethodenPointer funktioniert nämlich immer korrekt.
Vorteil: Die Erzeugung des Objectes durch
Delphi-Quellcode:
TTestObject.Create;
ist nicht nötig.

Andreas

himitsu 21. Jun 2021 17:07

AW: Problem mit Record-Parametern an MethodenPointer bzw. FunktionsPointer
 
Oder die Generics -> reference to

Alle "offiziellen" Möglichkeiten, ohne an der automatischen Typbehandlung rumzupfuschen:
Delphi-Quellcode:
type
  TTest = class
    function A(): Boolean;
    function B(): Boolean; virtual; // bzw. overload oder auch dynamic
    class function C(): Boolean;
    class function D(): Boolean; virtual; // bzw. overload oder auch dynamic
    class function E(): Boolean; static;
  end;
 
function F(): Boolean;

{
  E entspricht F
  A = C (nur eben TClass statt TObject, beim Self)
  A und C auch mit Data/Self=NIL möglich
}

var
  X: function(): Boolean; // E oder F
  Y: function(): Boolean of object; // A, B, C oder D
  Z: reference to function(): Boolean; // alles, inkl. anonymer Methoden (nicht direkt als VAR ... muß einen TYPE haben)
  O: TTest;
  L: class of TTest;


O := TTest.Create;

X := TTest.E; // oder L.E oder O.E inkl. O=nil
X := F;
Y := O.A; // auch O=nil
Y := O.B;
Y := TTest.C; // oder L.C oder O.C inkl. L=nil
Y := TTest.D; // oder L.D oder O.D
Z := egal was

Andreas13 21. Jun 2021 18:15

AW: Problem mit Record-Parametern an MethodenPointer bzw. FunktionsPointer
 
Danke, Himitsu!
Ich werde versuchen, Deinen recht konzentrierten Code zu verstehen. Leider habe ich noch etliche Lücken bezüglich Generics, anonymer Methoden etc. :oops:
Gruß, Andreas

himitsu 21. Jun 2021 18:28

AW: Problem mit Record-Parametern an MethodenPointer bzw. FunktionsPointer
 
Bei "reference to procedure" werden intern quasi die Generics verwendet ... du selbst hast hier nichts damit zu tun ... macht alles der Compiler von selbst.

freimatz 22. Jun 2021 15:05

AW: Problem mit Record-Parametern an MethodenPointer bzw. FunktionsPointer
 
Ja genau. Einfach "reference to" dazu und Hirn abschalten :-)


Alle Zeitangaben in WEZ +1. Es ist jetzt 12:05 Uhr.
Seite 1 von 2  1 2      

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz