Übergabe Funktion an Constructor

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
Antworten
Joh
Lazarusforum e. V.
Beiträge: 309
Registriert: Sa 26. Mai 2012, 17:31
OS, Lazarus, FPC: Win 10 (L 2.2.6 x64 FPC 3.2.2)
CPU-Target: 64Bit

Übergabe Funktion an Constructor

Beitrag von Joh »

Moin,

wenn ich eine Komponente im Code erstelle, kann ich ja die Ereignisprozedur mit

Code: Alles auswählen

komponente.OnChange := @setFilter;
zuweisen.
Wie übergebe ich dann die Funktion im Constructor, wenn ich die Ereignisprozedur mit übergeben will?

Der Aufruf

Code: Alles auswählen

constructor Create (AOwner: TComponent; ...; ptr: Pointer);
mit

Code: Alles auswählen

sf := TmyFeld.Create(self, ..., @setFilter);
wirft mit einen Error: Incompatible type for arg no. 4: Got "<procedure variable type of procedure(TObject) of object;Register>", expected "Pointer".
Bisher war ich der Meinung, ein @setFilter entspricht einem Pointer auf die Funktion setFilter. Kann ich überhaupt die Prozedur übergeben?
Oder wäre der passendere Weg, im Construktor über AOwner.setFilter zu gehen?

Joh

(ich befürchte, man merkt, das hier jemand, der eigentlich prozedural programmiert, versucht, Objekte zu erzeugen...)
just my two Beer

Benutzeravatar
Zvoni
Beiträge: 414
Registriert: Fr 5. Jul 2024, 08:26
OS, Lazarus, FPC: Windoof 10 Pro (Laz 2.2.2 FPC 3.2.2)
CPU-Target: 32Bit
Wohnort: BW

Re: Übergabe Funktion an Constructor

Beitrag von Zvoni »

Ein OnChange-Ereignis hat ja in der Regel nen Datentyp "TNotifyEvent"
https://lazarus-ccr.sourceforge.io/docs ... event.html

Ungetestet

Code: Alles auswählen

sf := TmyFeld.Create(self, ..., Pointer(@setFilter));  //Harter Cast
Damit brauchst du aber wieder einen Cast zurück im Constructor, glaub ich zumindest

Oder das versuchen:

Code: Alles auswählen

constructor Create (AOwner: TComponent; ...; ptr: TNotifyEvent);
Ein System sie alle zu knechten, ein Code sie alle zu finden,
Eine IDE sie ins Dunkel zu treiben, und an das Framework ewig zu binden,
Im Lande Redmond, wo die Windows drohn.

Joh
Lazarusforum e. V.
Beiträge: 309
Registriert: Sa 26. Mai 2012, 17:31
OS, Lazarus, FPC: Win 10 (L 2.2.6 x64 FPC 3.2.2)
CPU-Target: 64Bit

Re: Übergabe Funktion an Constructor

Beitrag von Joh »

Zvoni hat geschrieben: Do 10. Jul 2025, 12:19 Oder das versuchen:

Code: Alles auswählen

constructor Create (AOwner: TComponent; ...; ptr: TNotifyEvent);
Danke! so funktionierts. Noch so schön :-)
just my two Beer

Benutzeravatar
Zvoni
Beiträge: 414
Registriert: Fr 5. Jul 2024, 08:26
OS, Lazarus, FPC: Windoof 10 Pro (Laz 2.2.2 FPC 3.2.2)
CPU-Target: 32Bit
Wohnort: BW

Re: Übergabe Funktion an Constructor

Beitrag von Zvoni »

Joh hat geschrieben: Do 10. Jul 2025, 13:09
Zvoni hat geschrieben: Do 10. Jul 2025, 12:19 Oder das versuchen:

Code: Alles auswählen

constructor Create (AOwner: TComponent; ...; ptr: TNotifyEvent);
Danke! so funktionierts. Noch so schön :-)
Denk dran: Das funktioniert dann insoweit, sofern die Events alle den gleichen Typ haben (TNotifyEvent).
Sollte ein Event mit einem anderen Datentyp ins Spiel kommen (andere Arguments-Liste), ist wohl das sinnvollste einen Constructor-Overload pro Datentyp zu machen
Ein System sie alle zu knechten, ein Code sie alle zu finden,
Eine IDE sie ins Dunkel zu treiben, und an das Framework ewig zu binden,
Im Lande Redmond, wo die Windows drohn.

Warf
Beiträge: 2158
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Übergabe Funktion an Constructor

Beitrag von Warf »

Joh hat geschrieben: Do 10. Jul 2025, 12:10 Bisher war ich der Meinung, ein @setFilter entspricht einem Pointer auf die Funktion setFilter. Kann ich überhaupt die Prozedur übergeben?
Oder wäre der passendere Weg, im Construktor über AOwner.setFilter zu gehen?
Es gibt verschiedene arten von Funktionspointern die sich in Kontext unterscheiden. Reine Funktionspointer ohne Kontext zeigen einfach nur auf ein stück Code. Die haben prozeduren typen:

Code: Alles auswählen

type
  TMyProcPointer = procedure(Arg: Integer);
  TMyFuncPointer = function(Arg: Integer): Integer);
Dann gibt es Methodenpointer, die zeigen auf Methoden von Klassen. Eine Methode wird immer im Kontext eines Objektes aufgerufen, was man über Self referenzieren kann. Intern sind methoden pointer tatsächlich einfach 2 pointer, einer zum Code und einer zum Objekt:

Code: Alles auswählen

type
  TMyProcMethodPointer = procedure(Arg: Integer) of object;
  TMyFuncMethodPointer = function(Arg: Integer): Integer of object;
Und es gibt in Pascal noch nested Funktionen, das sind Funktionen im Kontext einer anderen Funktion, die können z.B. auf die Lokalen Variablen der umgebenden Funktion oder deren Parameter zugreifen. Ein Nested Funktionspointer enthält einen Pointer auf den Stack der umgebenden funktion, und den Pointer in den Code:

Code: Alles auswählen

type
  TMyNestedProcPointer = procedure(Arg: Integer) is nested;
  TMyNestedFuncPointer = function(Arg: Integer): Integer is nested;
Zu guter letzt gibts dann noch Funktions Referenzen, das sind Closures die ihren state in einem eigenen Objekt capturen. Intern sind das eigentlich einfach Objekte mit einer Invoke methode. Alle oben genannten funktionspointer typen können in eine Funktionsreferenz umgewandelt werden wobei der State zu dem Objekt hinzugefügt wird:

Code: Alles auswählen

type
  TMyNestedProcPointer = reference to procedure(Arg: Integer);
  TMyNestedFuncPointer = reference to function(Arg: Integer): Integer;
Die 3 basis funktionspointer (raw, method und nested) sind jeweils basisobjekte, im raw funktionspointer sind im Grunde Pointer und method und Nested sind im grunde ein Record mit 2 pointern.
Funktionsreferenzen erstellen eine instanz einer eigenen Klasse die auf dem Heap liegt, damit ist mit denen immer eine Memory Allokation verbunden und sie sind damit deutlich resourcen aufwendiger. Dafür vereinheitlichen sie alle drei konzepte und durch den State Capture als Closure bleibt der State auch über das lebensende der erstellenden Funktion erhalten.


PS: Anonymous Functions sind übrigens einfach nur nested procedures (wenn sie auf state zugreifen, sonst sogar raw), und sind daher entweder als nested(raw) funktionspointer oder funktionsreferenz darstellbar

Mathias
Beiträge: 6974
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Übergabe Funktion an Constructor

Beitrag von Mathias »

Code: Alles auswählen

type
  TMyProcPointer = procedure(Arg: Integer);
  TMyFuncPointer = function(Arg: Integer): Integer);
Dies ist die klassischste Variante, welche auch sehr viel bei C-Bindungen verwendet werden.
Dabei ist es wichtig, das man noch ein cdecl hinzufügt.
Das sieht dann so aus:

Code: Alles auswählen

type
  TMyProcPointer = procedure(Arg: Integer); cdecl;
  TMyFuncPointer = function(Arg: Integer): Integer); cdecl;
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Antworten