![]() |
Methodpointer - wie funktioniert's?
Hi @ll,
folgender Code:
Delphi-Quellcode:
Wenn ich bei dieser Konstruktion doStuff aufrufe, klappt das nicht ganz so, wie ich mir das vorstelle...
type
test =class function tut(arg:string):Boolean; end; implementation function test.tut(arg:string):Boolean; begin result :=true; end; function NichtMethode(arg:string):Boolean; begin result :=true; end; function doStuff() var p:pointer; t:test; begin t :=test.Create; p :=@test.tut; //Compilerfehler: Variable required p :=@NichtMethode; //funktioniert. end; Wieso? Wie macht man's richtig? EDIT:
Delphi-Quellcode:
(Also Zugriff als static) funktioniert...
p :=test.tut
Für meinen Fall reicht das sogar... Aber in anderen Fällen...? Wie macht man es, wenn man self haben will? EDIT2: Nein... funktioniert doch nicht... Denn wenn ich den Pointer wieder zu meiner Funktion caste und dann verushc edie Funktion aufzurufen, bekomme ich einen Compilererror: F2084 Internal Error: C4905 Das ist natürlich sehr informativ *stöhn* |
Re: Methodpointer - wie funktioniert's?
Zitat:
was genau stellst Du Dir denn vor? Ich meine klar, Du möchtest Die Adresse einer Methode, aber warum? Erstmal vorweg, Methodenzeiger sind typisierte Zeiger, die auf eine Methode mit bestimmter Signatur zeigen. Du könntest z.B. mit
Delphi-Quellcode:
einen Datentypen erzeugen, dem Du dann auch wirklich (und ohne Probleme) die Adresse der Funktion zuweisen kannst. Für deinen Fall klappt das einfach nicht, da Delphi hier nicht weiß, dass Du dich auf die Adresse der Funktion und nicht auf die Adresse des Funktionsergebnisses beziehst. Ein anderes Problem dass Du hier hättest liegt in der Überladung einer Methode. Was soll dein Code liefern, wenn ich deine Klasse um folgendes erweiter?:
type
methodpointer = function (arg: String): Boolean of Object;
Delphi-Quellcode:
Wird jetzt die Adresse von Deiner oder meiner Funktion in p gespeichert? Bei dem Datentypen wird dies eindeutig Durch die Signatur festgelegt.
function test.tut:Boolean;
begin result :=true; end; Wie gesagt, eigentlich ist hier wichtiger zu wissen warum Du überhaupt nach Methodenzeigern suchst (und hast Du die Forensuche mal benutzt? :wink:) Gruß Der Unwissende |
Re: Methodpointer - wie funktioniert's?
Zitat:
Das mache ich mit dem Dictionary, welches ich unter dem Titel "Hash-tabellen" in der Code-lib gefunden habe :-). Dadrin speicher ich dann immer einen Pointer auf die zugehörige Funktion. Zitat:
methodenpointer(p)(args); Was auch astrein funktioniert, solange es sich um eine Funktion und nicht um eine Methode handelt. Zitat:
Zitat:
Aber auch das muss man doch irgendwie lösen können... Wie gesagt: Mit einfachen Funktionen, die keine Methoden sind, klappt es ja auch... Zitat:
|
Re: Methodpointer - wie funktioniert's?
Du kannst Methodenzeiger und Funktionszeiger nicht mischen. Ein Funktionzeiger ist 4 Byte groß (SizeOf(Pointer)) und ein Methodenzeiger ist 8 Byte (SizeOf(TMethod)) groß. Bei deinem Typecast nach Pointer verlierst du also 4 Bytes, die aber notwendig sind.
|
Re: Methodpointer - wie funktioniert's?
Zitat:
Ich müsste also entweder einen Pointer auf den Pointer nehmen, oder das Dictionary von Pointer auf Methodenpointer umschreiben...? |
Re: Methodpointer - wie funktioniert's?
Zitat:
|
Re: Methodpointer - wie funktioniert's?
Zitat:
Die Idee ist einfach und häufig nötig, da viele moderne Sprachen auf explizite Zeiger (zum Glück) verzichten. Die einfache Idee besteht darin, dass Du statt einem Methodenzeiger einfach eine abstrakte Klasse oder ein Interface erstellst, dass die Methode die Du aufrufen möchtest enthält. Diese Methode ist dabei abstrakt (also noch nicht implementiert). Für jede Methode die Du aufrufen möchtest erstellst Du dann einfach eine Klasse, die von der abstrakten Klasse erbt bzw. das entsprechende Interface implementiert. Da wo die Methoden aufgerufen werden, brauchst Du nur noch eine Instanz der abstrakten Klasse oder des Interface. Was hier wirklich hinter steckt kann Dir egal sein, der Aufruf der Methode führt natürlich zum Aufruf der tatsächlichen Implementierung.
Delphi-Quellcode:
Hier hättest Du jetzt zwei verschidene Implementierungen des Interfaces. Nun kannst Du einfach für jede Klasse jeweils eine Instanz erzeugen und in deiner Map speichern. Für jeden String, den Du parst, holst Du dir dann aus der Map einfach ein Interface, von dem Du weißt das Du es in ein IBase-Interface casten und die Methode doFoo aufrufen kannst.
type
IBase = interface procedure doFoo(const arg: String); // implizit abstract und public end; TFoo = class(TInterfacedObject, IBase) public procedure doFoo(const arg: String); end; TBar = class(TInterfaceObject, IBase) public procedure doFOo(const arg: String); end; .... Ok, glaube die Maps arbeiten lieber mit Objekten zusammen, da würde sich eventuelle doch eine abstrakte Klasse besser machen. Ich hoffe jedenfalls, dass die Idee grob klar ist. Entspricht einfach nur der OOP-Variante eines Methodenzeigers (die Idee ist natürlich komplett die gleiche) |
Re: Methodpointer - wie funktioniert's?
Das Grundsätzliche Prinzip von Interfaces ist mir klar (bis jetzt habe ich aber Interfaces noch nie in Delphi verwendet. In Java aber schon).
Nur ist mir noch nicht ganz klar, wie mir das hilft... Bzw. wie du meinst, dass ich die Interfaces sinnvoll integrieren kann.
Delphi-Quellcode:
Isin = interface
procedure sin(const arg: extended); end; Icos = interface procedure cos(const arg: extended); end; ... TMyClass =class(Icos, Isin, ...) procedure cos(const arg: extended); procedure sin(const arg: extended); ... end; function callMyFunc(name: string) begin //und jetzt? Ich will die Funktion mit dem name den ich übergeben habe aufrufen... //aber wie helfen mir dabei die Interfaces? end; |
Re: Methodpointer - wie funktioniert's?
Zitat:
Du erzeugst jetzt also erstmal von jedem Interface (bzw. von der implementierenden Klasse) eine Instanz. Ah, seh gerade was Du falsch verstanden hast. Du hast das mit der Anzahl der Klasse und Interfaces etwas vertauscht.
Delphi-Quellcode:
Gut, wie die zu implementieren sind ist klar. Jetzt kommt die eigentliche Idee, statt Zeiger auf eine Funktion direkt im Dictionary zu speichern, legst Du nun die Interfaces ab. Da ich gerade nicht weiß wie dein Dictionary aufgebaut ist (dass, das Du verwendest) gehe ich der Einfachheit halber einfach von einer Klasse TDictionary aus, der ich mittels add(String, Interface) ein Element hinzufügen kann und die per Lookup(String): Interface mir ein Interface zurück gibt (musst Du dann nur entsprechend anpassen).
type
IFunction = interface function doWork(const argument: Extended): TResultType; end; // Gut, an der Benennung könnte man arbeiten, aber Du machst das schon! TCos = class(TInterfacedObject, IFunction) public function doWork(const argument: Extended): TResultType; end; TSin = class(TInterfacedObject, IFunction) public function doWork(const argument: Extended): TResultType; end;
Delphi-Quellcode:
Hier hast Du also den Vorteil, dass Du ganz unterschiedliche Klassen im Dictionary abspeicherst, die alle das Interface IFunction implentieren. D.h. hier also, dass Du weißt, dass alles was aus dem Dictionary kommt eine Funktion doWork hat (Ergebnis und Funktionsparameter stehen auch fest). So realisiert man z.B. auch Callbacks in Java (da es ja keine andere Möglichkeit gibt). Ich weiß nicht ob Du in Java mal mit Swing gearbeitet hast? Könnte Dich dann an die Ereignis-Benachrichtigung (einen Listener) erinnern.
// füllen des Dictionary
var buffer: IFunction; begin GlobalDict := TDictionary.Create(...); buffer := TSin.Create; GlobalDict.Add('sin', buffer); buffer := TCos.Create; GlobalDict.Add('cos', buffer); // .... end; // und jetzt das eigentliche Aufrufen: function callMyFunc(name: string): FunctionResult; var funcWrapper: IFunction; begin funcWrapper := IFunction(GlobalDict.Lookup(name)); result := funcWrapper.doWork(ExtendedArgument); end; Hoffe ist etwas klarer wie ich das meinte (und das nicht wieder ein roter Kasten fehlt). |
Re: Methodpointer - wie funktioniert's?
Hi,
der harte TypeCast dürfte aber zu einer Exception führen. Besser wäre es so:
Delphi-Quellcode:
Voraussetzung für den Einsatz der Funktion Supports ist aber, daß die Interfaces eine eindeutige GUID besitzen:
function callMyFunc(name: string): TResultType;
var funcWrapper: IFunction; begin if Supports(GlobalDict.Lookup(name), IFunction, funcWrapper) then result := funcWrapper.doWork(ExtendedArgument) else result := ... end;
Delphi-Quellcode:
Diese kann man im Editor über die Tastenkombination [Umsch][Strg][G] leicht erzeugen lassen.
type
IFunction = interface ['{4C30EFEE-10A6-4B88-919E-9CD12673572F}'] function doWork(const argument: Extended): TResultType; end; Gruß Hawkeye |
Alle Zeitangaben in WEZ +1. Es ist jetzt 14:30 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