Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Beispiel für Attribut an einem Methoden/Funktionsparameter (https://www.delphipraxis.net/212013-beispiel-fuer-attribut-einem-methoden-funktionsparameter.html)

mjustin 2. Dez 2022 10:17

Delphi-Version: 5

Beispiel für Attribut an einem Methoden/Funktionsparameter
 
Der Thread "boolean function: muss result := false gesetzt werden?" brachte mich zu einer älteren Fragestellung zurück:

Is it possible to use Attributes on Delphi method arguments?

Die Antwort war Ja (es war nur undokumentiert) und in der Antwort auf Stackoverflow war auch ein Beispiel. Dieses zeigte: RTTI findet das Attribut der Methode.

Nun meine Zusatzfrage: wie kann ich den konkreten Wert des Parameters, also zum Aufrufzeitpunkt, ermitteln?

Ich vermutete zuerst, dass zum Beispiel in Spring4D etwas in dieser Richtung enthalten ist (leider nein). Ich denke, es wäre ein guter Eintrag für die Codebibliothek. Da ich nun gelegentlich auch in einer neueren Delphi-Version unterwegs bin, würde ich gerne diese kleine Aufgabe lösen:
  1. ein neues Attribut definieren: [NotNil]
  2. Parameter, die dieses Attribut enthalten, werden zur Laufzeit geprüft.
  3. Ist der übergebene Parameterwert nil, wird eine Exception ausgelöst.
Dazu suche ich nun Code, um per RTTI den konkreten Parameterwert auszulesen, also den Wert, der in dem attribuierten Parameter steht.

Beispiel für die Verwendung des Attributs:

Delphi-Quellcode:
procedure TMyClass.OnChangeMe([NotNil] ASender: TObject);
begin
  // hier ist ASender 'garantiert' nicht nil
  // ... mache etwas
end;

himitsu 2. Dez 2022 11:06

AW: Beispiel für Attribut an einem Methoden/Funktionsparameter
 
Bei direktem Aufruf, ist ein gezieltes
Delphi-Quellcode:
if Param=nil
oder ein
Delphi-Quellcode:
Assert(Assigned(Patem)
garantiert einfacher und vor allem auch "optimaler".

Aber z.B. für Schnittstellen, wo Funktionsaufrufe weitergeleitet oder Events mit externen/ungeprüften Parameter aufgerufen werden ...
Nja, am Ende wäre auch das die direkte Prüfung einfacher, aber vermutlich in den Zwischenebenen das RTTI auch nutzbar.


Wenn der Methoden-Aufruf nicht via RTTI vorgenommen würde und du bereits Deklaration und die Parameter in RTTI-freundlicher Form vorliegen,
* mußt du erstmal die Definition der Methode selbst reingeben oder aus der Codeposition versuchen zu finden (bei Events hat du den Typ bereits in der Hand)
* und dann die Parameter auch noch bekommen
* * mitten im Code nahezu unmöglich ... aber beim / vor dem Aufruf oder als RTTI-Hook im Aufruf ging es aber

TRttiProcedureType/TRttiMethodType/TRttiMethod.Invoke oder mit dem TVirtualMethodInterceptor



In diesem Fall würde ich dir empfehlen, dieses [NotNil] bzw. ein [Check(...)] als FeatureRequst dem Hersteller zu unterbreiten
damit der Compiler automatisiert eine
Delphi-Quellcode:
if Assigned
einfügt, ähnlich wie bei [Weak] , [Volatile] und [Ref] oder den Index- und Überlaufprüfungen durch impliziten zusätzlichen Code.

Der schöne Günther 2. Dez 2022 12:00

AW: Beispiel für Attribut an einem Methoden/Funktionsparameter
 
Ich wüsste jetzt nicht, wie man herausbekommt, in welcher Methode man aktuell grade steckt. Wenn man die
Delphi-Quellcode:
TRttiMethod
kennt, kann man sich da ja weiterhangeln.

Eine Möglichkeit wäre ein TVirtualMethodInterceptor der eine bestehende Instanz wrapped und vor/nach jedem Methodenaufruf z.B. die Parameter prüft, verändert, oder was auch immer. Setzt halt nur virtuelle Methoden vorraus und macht das ganze bestimmt nicht schneller als ein hartkodiertes
Delphi-Quellcode:
if not Assigned(p) then raise EArgumentNilException
.

himitsu 2. Dez 2022 12:17

AW: Beispiel für Attribut an einem Methoden/Funktionsparameter
 
Nja, im Prinzip in der Methode, welche kurz vor der aktuellen CodePosition beginnt, aber sicher ist sowas nicht.
Via RTTI alle Klassen+Methoden (nicht normale Prozeduren/Funktionen) durchlaufen und die Adressen vergleichen
oder über Debuginfos.

Ein __METHOD__, __FUNCTION__, __LINE__ bzw. eigene Preprozessor gibt es ja leider im "Delphi" nicht.
https://docwiki.embarcadero.com/RADS...inierte_Makros

mjustin 2. Dez 2022 12:20

AW: Beispiel für Attribut an einem Methoden/Funktionsparameter
 
Danke für das Feedback - ja, der TVirtualMethodInterceptor ist hier der Lösungsansatz, und ich stimme 100% zu dass ein Assert() auch funktioniert.

Im verlinkten Stackoverflow-Artikel war das Anwendungsbeispiel etwas fortgeschrittener, es sollte ein REST-Request-Parameter extrahiert werden. Durch die Annotation des Parameters mappt man dessen konkreten Wert auf den Anteil des Requests, in dem dieser enthalten ist. Das kann ein Path-, ein Query- oder ein Body-Parameter sein.
Dann könnte man Methodensignaturen dieser Art verwenden:
Delphi-Quellcode:
[GET]
function TMyResource.GetArtikelbestand([PathParam] ArtikelNr: string; [QueryParam] Farbe: string = ''): TArtikelbestandResponse;
begin
  // in ArtikelNr steht nun die aus dem HTTP Request extrahierte Artikelnummer, und in Farbe entsprechend die Artikelfarbe (optional).
end;
Das würde dann aus der URL z.B. /api/v1/artikel/12345?farbe=blau die Parameter ermitteln und an die Methode weitergeben. Auch hier wäre ein Intercept notwendig, diesmal ausserdem ein Verändern der Parameterwerte, nicht nur ein Lesen.

himitsu 2. Dez 2022 12:38

AW: Beispiel für Attribut an einem Methoden/Funktionsparameter
 
Sowas ähnliches geh auch im DataSnap bzw. RADServer von Emba,
welcher ebenfalls eine REST-Schnittstelle bietet.

Dazu dann auch noch Dinge ala Benutzerverwaltung/Zugriffsbeschränlungen einzelner Klassen oder Methoden.



Wie gesagt, hier wird die Funktion am Ende auf etwas Anderes umgeleitet und über das Framework aufgerufen, wo man vor dem Aufruf dann auch solche Prüfungen machen kann ... vor der Funktion und nicht erst darin.



Beim TVirtualMethodInterceptor wird die TypInfo der Klasse geklont, in der abgeleiteten VMT mit den InterceptorCalls die virtuellen Methoden umgeleitet und dann in der Instanz der KlassenZeiger umgebogen.
Leider auch bei ALLEN Virtuellen und nicht nur die Gewollten (könnte man aber anschließend selber wieder an der VMT rumfummeln/zurückbiegen).

beim Aufruf:
Adressen, Register und Stack in TInvokeInfo kopieren
intern mehrmals rumreichen
und am Ende wieder alles in Register/Stack schreiben und der CALL auf die originle Adresse.


Alle Zeitangaben in WEZ +1. Es ist jetzt 18:34 Uhr.

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