Functionpointer auf genestete Methoden [gelöst]

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
Benutzeravatar
corpsman
Lazarusforum e. V.
Beiträge: 1630
Registriert: Sa 28. Feb 2009, 08:54
OS, Lazarus, FPC: Linux Mint Mate, Lazarus GIT Head, FPC 3.0
CPU-Target: 64Bit
Wohnort: Stuttgart
Kontaktdaten:

Functionpointer auf genestete Methoden [gelöst]

Beitrag von corpsman »

Hallo zusammen,

Eigentlich möchte ich ja eine Anonyme Funktion nutzen, doch der FPC 3.2.2 der mit Linux Mint Mate kommt kann dass wohl noch nicht :(.

Also versuche ich mich an dem "nächst" unschöneren, einen Pointer auf eine Genestete Function, doch noch mag mich der Compiler nicht..

Code: Alles auswählen


Type
  TIntegerConvertFunc = Function(s: String): integer; // <-- Wie muss ich das anpassen, dass es "nested register" sagt ?

Function Ausen(s: String): integer;
Begin
  result := StrToInt(s);
End;

Procedure TForm1.Button1Click(Sender: TObject);

  Function innen(s: String): integer;
  Begin
    result := StrToInt(s);
  End;
Var
  myFunction: TIntegerConvertFunc;
Begin
  myFunction := @innen; // <-- Das hier geht nicht
  myFunction := @Ausen; // <-- Das hier geht
End;
                   
Habt ihr ideen ?
Zuletzt geändert von corpsman am Fr 4. Okt 2024, 14:53, insgesamt 1-mal geändert.
--
Just try it

Benutzeravatar
corpsman
Lazarusforum e. V.
Beiträge: 1630
Registriert: Sa 28. Feb 2009, 08:54
OS, Lazarus, FPC: Linux Mint Mate, Lazarus GIT Head, FPC 3.0
CPU-Target: 64Bit
Wohnort: Stuttgart
Kontaktdaten:

Re: Functionpointer auf genestete Methoden

Beitrag von corpsman »

*g* man muss nur wissen wo man suchen muss, in der Unit MTProcs wird es gezeigt ;)

Code: Alles auswählen


 {$ModeSwitch nestedprocvars}                // -- Der muss gesetzt sein
 
 ..
 Type
  TIntegerConvertFunc = Function(s: String): integer Is nested; 


Function Ausen(s: String): integer;
Begin
  result := StrToInt(s);
End;

Procedure TForm1.Button1Click(Sender: TObject);

  Function innen(s: String): integer;
  Begin
    result := StrToInt(s);
  End;
Var
  myFunction: TIntegerConvertFunc;
Begin
  myFunction := @innen;
  //  myFunction := @Ausen;
End;


und schon gehts ;).
--
Just try it

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

Re: Functionpointer auf genestete Methoden

Beitrag von Warf »

Ohne Function References (die mit der nächsten FPC Version kommen) passiert allerdings keine state capture, was bedeutet das wenn die nested Funktionen auf variablen oder Parameter der Parent Funktion zugreifen sobald du die Funktion verlässt, dir das komplett um die Ohren fliegt weil du dann irgendwo auf den Stack zugreifst.

PS:
corpsman hat geschrieben: Fr 4. Okt 2024, 10:32 Eigentlich möchte ich ja eine Anonyme Funktion nutzen
Warum? Ob du jetzt schreibst:

Code: Alles auswählen

procedure Test;
var
  x: Integer;
  
procedure PrintX;
begin
  WriteLn(x);
end;

begin
  DoFunPtrStuff(@PrintX);
end;
Oder du schreibst:

Code: Alles auswählen

procedure Test;
var
  x: Integer;

begin
  DoFunPtrStuff(procedure
begin
  WriteLn(x);
end);
end;
Du schreibst ziemlich genau den selben code, bis auf ein paar ";" sparst du nix (was man vor allem daran merkt das ich das zweite beispiel damit erzeugt hab einfach das erste zu kopieren und nur die prozedur zu bewegen). Nur das erste code deutlich lesbarer ist, weil du nicht verschiedne Funktionalitäten schachtelst

Benutzeravatar
fliegermichl
Lazarusforum e. V.
Beiträge: 1659
Registriert: Do 9. Jun 2011, 09:42
OS, Lazarus, FPC: Lazarus Fixes FPC Stable
CPU-Target: 32/64Bit
Wohnort: Echzell

Re: Functionpointer auf genestete Methoden

Beitrag von fliegermichl »

Oh Mann,
ich hab mir da einen abgebrochen mit z.B. dem da: (TMuMPersistent ist eine von TList abgeleitete Klasse

Code: Alles auswählen

function TMuMPersistent.FirstThat (Test: Pointer) : Pointer;
var
  item : Pointer;
  i : Integer;
begin
  result := nil;
  i := 0;
  while i < count do
  begin
    item := items[i];
    if assigned (item) then
    if boolean(byte(ptruint(callPointerIntLocal(test, get_caller_frame(get_frame), item, i)))) then
    begin
     Result := Item;
     exit;
    end;
    inc (i)
  end;
end;
Die mit is nested deklarierte Prozedur oder Funktion bekommt auch wunderbar den Stackframe der aufrufenden Prozedur, selbst wenn sie sich selbst rekursiv aufruft.
Beachte hier den rekursiven Aufruf von @all.
Klappt wunderbar.
Hier ein komplettes Codebeispiel. Das Formular braucht nur einen Button und ein Memo.

Code: Alles auswählen

unit Unit1;

{$mode objfpc}{$H+}
{$ModeSwitch nestedprocvars}                // -- Der muss gesetzt sein

interface

uses
  Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls;

type
  TMyBase = class;
  TForEachProcedure = procedure(Item : TMyBase) is Nested;
  TFirstThatFunction = function(Item : TMyBase) : boolean is Nested;

  { TMyBase }

  TMyBase = class ( TList )
    fName : string;
    constructor Create(aName : string);
    function  Get(Index : Integer) : TMyBase;
    procedure Put(Index : Integer; Item : TMyBase);
    procedure ForEach(All : TForEachProcedure);
    function  FirstThat(Test : TFirstThatFunction) : TMyBase;
    property  Items[Index : Integer] : TMyBase read Get write Put; default;
  end;


  { TForm1 }

  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    fData : TMyBase;
  public

  end;

var
  Form1: TForm1;

implementation

{$R *.lfm}

{ TMyBase }

constructor TMyBase.Create(aName: string);
begin
  inherited Create;
  fName := aName;
end;

function TMyBase.Get(Index: Integer): TMyBase;
begin
  Result := TMyBase(inherited Get(Index));
end;

procedure TMyBase.Put(Index: Integer; Item: TMyBase);
begin
  inherited Put(Index, Item);
end;

procedure TMyBase.ForEach(All: TForEachProcedure);
var cnt : Integer;
begin
  for cnt := 0 to Count - 1 do
    All(Items[cnt]);
end;

function TMyBase.FirstThat(Test: TFirstThatFunction): TMyBase;
var i : integer;
begin
  for i := 0 to Count - 1 do
  begin
    Result := Items[i];
    if Test(Result) then exit;
  end;
  Result := nil;
end;

{ TForm1 }

procedure TForm1.FormCreate(Sender: TObject);
var sub : TMyBase;
begin
  fData := TMyBase.Create('Basis');
  fData.Add(TMyBase.Create('SubItem 1'));
  sub := TMyBase.Create('SubItem 2');
  fData.Add(sub);
  sub.Add(TMyBase.Create('sub sub'));
  fData.Add(TMyBase.Create('SubItem 3'));
  fData.Add(TMyBase.Create('SubItem 4'));
end;

procedure TForm1.Button1Click(Sender: TObject);
var ParentName : string;

  procedure All(Item : TMyBase);
  var MyParent : string;
  begin
    Memo1.Lines.Add(ParentName + ' ' + Item.fName);
    MyParent := ParentName;
    ParentName := ParentName + ' ' + Item.fName;
    Item.ForEach(@All);
    ParentName := MyParent;
  end;

  function test(Item : TMyBase) : boolean;
  begin
    Result := Item.fName = ParentName;
  end;

var Item : TMyBase;
begin
  ParentName := fData.fName;
  fData.ForEach(@All);
  ParentName := fData[2].fName;
  Item := fData.FirstThat(@test);
  if (Item <> nil) then
    Memo1.Lines.Add('Found subitem with same Name' + Item.fName)
  else
    Memo1.Lines.Add('Nothing found');
end;

end.
In dem Memo steht nachher:

Code: Alles auswählen

Basis SubItem 1
Basis SubItem 2
Basis SubItem 2 sub sub
Basis SubItem 3
Basis SubItem 4
Found subitem with same NameSubItem 3
und das allerbeste ist dabei, daß ich die Typprüfungen des Compilers nicht mit dem Pointergefrickel umgehen muß.

Benutzeravatar
corpsman
Lazarusforum e. V.
Beiträge: 1630
Registriert: Sa 28. Feb 2009, 08:54
OS, Lazarus, FPC: Linux Mint Mate, Lazarus GIT Head, FPC 3.0
CPU-Target: 64Bit
Wohnort: Stuttgart
Kontaktdaten:

Re: Functionpointer auf genestete Methoden

Beitrag von corpsman »

*g*, danke für die Rege Teilname, mein Konkretes Beispiel sieht so aus:

Code: Alles auswählen

Procedure TPixelEditor.OpenGLControlMouseDown(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);

  Procedure SetColorAt(i, j: integer);
  Begin
    fImage.SetColorAt(i, j, fAktualLayer, fCursor.LeftColor.Color);
  End;

Begin
  fScrollInfo.ScrollPos := point(x, y);
  fCursor.PixelPos := CursorToPixel(x, y);
  fCursor.Pos := point(x, y);
  If ssLeft In shift Then Begin
    If (fCursor.PixelPos.X <> -1) And (Not ColorPicDialog.Visible) Then Begin
      DoCursorOnPixel(fCursor, @SetColorAt);
    End;
  End;
  If ssRight In shift Then Begin
  End;
  UpdateInfoLabel();
End;                 
Mit Anonymen Funktionen hätte dass dann irgendwie so aussehen können:

Code: Alles auswählen

Procedure TPixelEditor.OpenGLControlMouseDown(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);

Begin
  fScrollInfo.ScrollPos := point(x, y);
  fCursor.PixelPos := CursorToPixel(x, y);
  fCursor.Pos := point(x, y);
  If ssLeft In shift Then Begin
    If (fCursor.PixelPos.X <> -1) And (Not ColorPicDialog.Visible) Then Begin
      DoCursorOnPixel(fCursor, 
      Procedure (i, j: integer);
      Begin
        fImage.SetColorAt(i, j, fAktualLayer, fCursor.LeftColor.Color);
      End;
      );
    End;
  End;
  If ssRight In shift Then Begin
  End;
  UpdateInfoLabel();
End;                 
Was da nun besser ist, darüber lässt sich natürlich streiten ;).

By the Way, wer den ganzen Code sehen will, der liegt unter https://github.com/PascalCorpsman/mini_ ... ixelEditor ab, wahrscheinlich stelle ich teile davon morgen aufm Lazarustreffen vor *g*
--
Just try it

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

Re: Functionpointer auf genestete Methoden [gelöst]

Beitrag von Mathias »

Ich wollte es gerade ausprobieren, aber bei mir geht es nicht.

Code: Alles auswählen

program project1;

  {$ModeSwitch nestedprocvars}

type
  TMyFunc = procedure(const s: string);

  procedure func1(const s: string);
  begin
    WriteLn('aussen');
  end;

  procedure main;

    procedure func2(const s: string);
    begin
      WriteLn('innen');
    end;

  var
    f: TMyFunc;
  begin
    f := @func1;
    f('123');
    f := @func2;  // Da motzt er
    f('456');
  end;

begin
  main;
end.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Benutzeravatar
fliegermichl
Lazarusforum e. V.
Beiträge: 1659
Registriert: Do 9. Jun 2011, 09:42
OS, Lazarus, FPC: Lazarus Fixes FPC Stable
CPU-Target: 32/64Bit
Wohnort: Echzell

Re: Functionpointer auf genestete Methoden [gelöst]

Beitrag von fliegermichl »

Mathias hat geschrieben: Sa 5. Okt 2024, 09:06 Ich wollte es gerade ausprobieren, aber bei mir geht es nicht.

Code: Alles auswählen

program project1;

  {$ModeSwitch nestedprocvars}

type
  TMyFunc = procedure(const s: string);
 ...
TMyFunc = procedure(const s : string) is nested;.
Dann geht es.

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

Re: Functionpointer auf genestete Methoden [gelöst]

Beitrag von Mathias »

TMyFunc = procedure(const s : string) is nested;.
Dann geht es.
Nach oben
Danke, jetzt gehts.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

PascalDragon
Beiträge: 963
Registriert: Mi 3. Jun 2020, 07:18
OS, Lazarus, FPC: L 2.0.8, FPC Trunk, OS Win/Linux
CPU-Target: Aarch64 bis Z80 ;)
Wohnort: München

Re: Functionpointer auf genestete Methoden [gelöst]

Beitrag von PascalDragon »

Functionpointer auf genestete Methoden
Kleines Nitpicking: als Methoden werden die Routinen bezeichnet, welche einen Self-Parameter haben. Verschachtelte Funktionen und Prozeduren haben keinen Self-Parameter (außer sie sind innerhalb einer Methode deklariert) und sind deshalb auch keine Methoden.
FPC Compiler Entwickler

Benutzeravatar
Niesi
Lazarusforum e. V.
Beiträge: 602
Registriert: So 26. Jun 2016, 19:44
OS, Lazarus, FPC: Linux Mint Cinnamon, Laz 4.1 Fpc 3.2.3 und allerlei mit FpcUpDeLuxe
Kontaktdaten:

Re: Functionpointer auf genestete Methoden [gelöst]

Beitrag von Niesi »

corpsman hat geschrieben: Fr 4. Okt 2024, 10:32 Hallo zusammen,

Eigentlich möchte ich ja eine Anonyme Funktion nutzen, doch der FPC 3.2.2 der mit Linux Mint Mate kommt kann dass wohl noch nicht :(.

...
Hast Du Dich schon einmal mit FpcUpDeLuxe beschäftigt? Ist sehr empfehlenswert, ich verwende Linux Mint mit FPC 3.3.1 / Lazarus 3.99 ...

Eine andere Frage: Wozu braucht mensch nested functions?

Ich habe mal gesucht: Eine nested function ist eine Funktion in einer Funktion, die nur nnerhalb des Gültigkeitsbereichs der äußeren Funktion sichtbar ist.

Wenn ich eine Funktion in einer Funktion definiere, dann ist das doch sowieso so, oder?
Wissen ist das einzige Gut, das sich vermehrt, wenn es geteilt wird ...

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

Re: Functionpointer auf genestete Methoden [gelöst]

Beitrag von Warf »

Niesi hat geschrieben: Di 8. Okt 2024, 08:05 Eine andere Frage: Wozu braucht mensch nested functions?
Zwei Gründe, zum einen Scoping, wenn eine Funktion nur im Kontext einer anderen Funktion benutzt wird machts keinen Sinn das die Funktion global sichtbar ist, daher definiert man sie im Perimeter der Aufrufenden funktion.

Zum anderen haben nested Funktionen zugriff auf lokale Variablen:

Code: Alles auswählen

procedure Test;
var
  x: Integer;

function SmallThanX(const i: Integer): Boolean;
begin
  Result := i<x;
end;
begin
  // Anderer Code, z.B. Array füllen
  filtered := Filter(@SmallerThanX, arr);
end;
Damit kann man z.B. hier der Filter funktion einen Funktionspointer übergeben der alle elemente rausfiltert die größer als die lokale variable X sind, obwohl die funktion filter X nicht kennt

Benutzeravatar
corpsman
Lazarusforum e. V.
Beiträge: 1630
Registriert: Sa 28. Feb 2009, 08:54
OS, Lazarus, FPC: Linux Mint Mate, Lazarus GIT Head, FPC 3.0
CPU-Target: 64Bit
Wohnort: Stuttgart
Kontaktdaten:

Re: Functionpointer auf genestete Methoden [gelöst]

Beitrag von corpsman »

Niesi hat geschrieben: Di 8. Okt 2024, 08:05
Hast Du Dich schon einmal mit FpcUpDeLuxe beschäftigt? Ist sehr empfehlenswert, ich verwende Linux Mint mit FPC 3.3.1 / Lazarus 3.99 ...
Also Anonyme Functionen wird es erst ab FPC 3.4.0 geben, FpcUpDeLuxe würde hier also nichts bringen. Und nein habe ich noch nicht, da ich mit Lazarus mehr als nur Glücklich bin ;).
Das Hauptargument für FpcUpDeLuxe ist ja das Crosscompiling und die Aktualität. Das Crosscompiling mache ich mit einem Docker, also kein Thema, und Lazarus habe ich die Head version, noch aktueller kann man eh nicht sein *g*.

Um wieder zurück zum Thema zu finden, wer in https://github.com/PascalCorpsman/mini_ ... _types.pas (ca Zeile 60 ) nachsieht bemerkt, dass ich durch refactoring das is Nested wieder los geworden bin und nun eine "Normale" Methode der TPixelEditor klasse übergeben kann 8)
--
Just try it

Benutzeravatar
Niesi
Lazarusforum e. V.
Beiträge: 602
Registriert: So 26. Jun 2016, 19:44
OS, Lazarus, FPC: Linux Mint Cinnamon, Laz 4.1 Fpc 3.2.3 und allerlei mit FpcUpDeLuxe
Kontaktdaten:

Re: Functionpointer auf genestete Methoden [gelöst]

Beitrag von Niesi »

Ähhhh - also: FpcUpDeLuxe ist ein Programm zur Installation von Lazarus mit Free-Pascal.

Du kannst dann verschiedene Versionen installieren - ist ein wirklich großartiges Tool, welches das Leben deutlich erleichtert.

https://github.com/LongDirtyAnimAlf/fpc ... e/releases

Auf "Show all 43 assets" klicken - ich nutze für Linux Mint die "fpcupdeluxe-x86_64-linux".

Mach Dir einen Ordner, leg das Programm rein und gib ihm das Recht, als Programm ausgeführt zu werden. Dann starten und Deine Version von Lazarus und Free-Pascal in einen Ordner Deiner Wahl installieren. Wenn Du das Paket nicht mehr brauchst: Einfach den Ordner löschen.

Ein weiteres Mal Danke an Alfred für diese tolle Tool ...
Wissen ist das einzige Gut, das sich vermehrt, wenn es geteilt wird ...

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

Re: Functionpointer auf genestete Methoden [gelöst]

Beitrag von Warf »

Niesi hat geschrieben: Di 8. Okt 2024, 13:58 https://github.com/LongDirtyAnimAlf/fpc ... e/releases

Auf "Show all 43 assets" klicken - ich nutze für Linux Mint die "fpcupdeluxe-x86_64-linux".

Mach Dir einen Ordner, leg das Programm rein und gib ihm das Recht, als Programm ausgeführt zu werden. Dann starten und Deine Version von Lazarus und Free-Pascal in einen Ordner Deiner Wahl installieren. Wenn Du das Paket nicht mehr brauchst: Einfach den Ordner löschen.

Ein weiteres Mal Danke an Alfred für diese tolle Tool ...
Man kann sich eine "dubiose" 11MB executable aus dem Internet laden, oder man machts einfach selbst mit 9 Befehlen:

Code: Alles auswählen

# Sources runterladen (shallow clone um Speicher zu sparen)
git clone --depth=1 https://gitlab.com/freepascal.org/FPC/source.git fpcsource
# Sourcen bauen mit platform FPC
make -C fpcsource all -j$(numcpus)
# Gebaute binaries in seperaten ordner installieren
make -C fpcsource install INSTALL_PREFIX=$(realpath ./fpc)
# Verbleibende buildartifakte entfernen um speicher zu sparen
git -C fpcsource clean -xdff
# fpc.cfg erstellen
fpcmkcfg -d basepath=$(realpath fpc/lib/fpc/3.3.1/) -o fpc/lib/fpc/etc/fpc.cfg
# Gebauten compiler linken
ln -s $(realpath fpc/lib/fpc/3.3.1/ppcx64) fpc/bin/ppcx64
# Lazarus sources runterladen (shllow clone um Speicher zu sparen)
git clone --depth=1 https://gitlab.com/freepascal.org/lazarus/lazarus.git
# Laztarus bauen
make -C lazarus all
# Lazarus config erstellen
echo "--pcp=$(realpath ./lazarus/config)" > lazarus/lazarus.cfg
PS: nicht das ich fpcup nicht vertraue, aber ich finde es irgendwie absurd sich irgendwelche (gigantischen) binaries runterzuladen für etwas was wirklich sehr sehr einfach ist selbst zu machen. Bonuspunkte das die installation mit dem code oben etwa 900 mb speicher einspart gegenüber fpcup und deutlich schneller geht

PS: Hab ein install script angehängt
Dateianhänge
install.sh
(767 Bytes) 107-mal heruntergeladen
Zuletzt geändert von Warf am Di 8. Okt 2024, 19:10, insgesamt 2-mal geändert.

Stevie
Beiträge: 173
Registriert: Di 27. Feb 2024, 22:40

Re: Functionpointer auf genestete Methoden [gelöst]

Beitrag von Stevie »

Das Hauptargument für FpcUpDeLuxe ist ja das Crosscompiling und die Aktualität.
Ich glaube, dass FPCUpDeluxe für die meisten einfach ein plattformübergreifendes Setup.exe auf Speed ist. Man kann 'Stable' bis 'Trunk' schnell mal eben eine Umgebung bauen und installieren und bei Bedarf noch irgendwelche Mucken wegpatchen. Und auf einem neuen Rechner besteht die einzige Vorbereitung darin, FPCUpDeluxe von der Github-Release-Page zu holen, zu starten und den gewünschten FPC- und Lazarus-Release auszuwählen. Dann Knopf drücken, warten und sich über ein Start-Icon auf dem Desktop freuen.

Natürlich kann man das alles (und bestimmt noch mehr) auch zu Fuß auf der Kommandozeile machen, aber das hier ist so einfach, dass man es auch einem Lazarus-Beginner zumuten kann. Und das ist sooooo wertvoll und wichtig für die Akzeptanz unter den Anfängern!

Antworten