Generics for Dummies

Für Fragen von Einsteigern und Programmieranfängern...
PascalDragon
Beiträge: 962
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: Generics for Dummies

Beitrag von PascalDragon »

fliegermichl hat geschrieben: Fr 22. Apr 2022, 09:24 Das andere Beispiel mit der generischen Sortierung ist mir zu hoch oder ich muss es wohl noch 10 mal lesen um es begreifen zu können :-)
Dabei ist das gerade ein recht einfaches Beispiel im Vergleich zu manch anderem, was man mit Generics anstellen kann...
FPC Compiler Entwickler

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

Re: Generics for Dummies

Beitrag von fliegermichl »

PascalDragon hat geschrieben: Fr 22. Apr 2022, 09:40
fliegermichl hat geschrieben: Fr 22. Apr 2022, 09:24 Das andere Beispiel mit der generischen Sortierung ist mir zu hoch oder ich muss es wohl noch 10 mal lesen um es begreifen zu können :-)
Dabei ist das gerade ein recht einfaches Beispiel im Vergleich zu manch anderem, was man mit Generics anstellen kann...
Naja, es heisst ja auch Generics for Dummies. Also scheine ich hier richtig zu sein :-)

Benutzeravatar
theo
Beiträge: 10920
Registriert: Mo 11. Sep 2006, 19:01

Re: Generics for Dummies

Beitrag von theo »

fliegermichl hat geschrieben: Fr 22. Apr 2022, 11:24 Naja, es heisst ja auch Generics for Dummies. Also scheine ich hier richtig zu sein :-)
Ich sag doch: Dummies brauchen keine Generics!
Aber auf mich will ja keiner hören... :lol:

hubblec4
Beiträge: 347
Registriert: Sa 25. Jan 2014, 17:50

Re: Generics for Dummies

Beitrag von hubblec4 »

Hi

Schön das es diess Thema schon gibt und das auch schon so viel Infos zusammengetragen wurden.

Ich selbst nutze Generics schon eine Weile für spezielle Listen von eigenen Klassen. Es ist einfach einfacher diese direkt aus der Liste als "korrekten" Typ zu erhalten.

Nach stundenlangen Suchen imNetz bin ich noch nicht auf die richtige Lösung gestossen und wollte deshalb hier mal nachfragen.

Für meine EBML lib bräuchte ich folgendes (ist ein c++ code)

Code: Alles auswählen

template <typename Type>
Type & GetChild(EbmlMaster & Master)
{
  return *(static_cast<Type *>(Master.FindFirstElt(EBML_INFO(Type), true)));
}
// call with
// MyDocType = GetChild<EDocType>(TestHead);
Für mich sieht das so als könnte man dies mit Generics nachbilden.

Code: Alles auswählen

generic function GetChild<T>(const Master: TEbmlMaster): T;
begin
  Result :=  T(Master.GetChild(T));
end; 
//call: // MyDocType = specialize GetChild<TDocType>(aMaster);
Ein Master Element hat eine Liste wo andere EBML-Typ-Elemente gespeichert sind.
All die Elemente sind von TEBMLElement abgeleitet und die Liste ist spezialisiert auf den Typ TEBMLElement.

Momentan muss ich das ganze wie folgt aufrufen:

Code: Alles auswählen

begin...
  MyDocType := TDocType(Master.GetChild(TDocType))
end;
Leider funktioniert der Generic Code nicht. Innerhalb der Generic function kann ich nicht mal auf die "const Master" zugreifen, obwohl ich im Funktionskopf den Type für "Master" festlegen kann: TEbmlMaster.

Ich nutze FPC 3.2.2 Lazarus 3.0

Was mache ich falsch?

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

Re: Generics for Dummies

Beitrag von Mathias »

Ich nutze FPC 3.2.2 Lazarus 3.0
Ist die volle Unterstützung von generic nicht FPC 3.3.x vorbehalten ?
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

hubblec4
Beiträge: 347
Registriert: Sa 25. Jan 2014, 17:50

Re: Generics for Dummies

Beitrag von hubblec4 »

Ich habe mir eben mit FPdeluxe die neuste Lazarus Version und FPC 3.3.1 installiert, aber auch da geht der Generic Code nicht.

"Master" ist nach wie vor nur Text, nix was der Compiler kennt.

PascalDragon
Beiträge: 962
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: Generics for Dummies

Beitrag von PascalDragon »

hubblec4 hat geschrieben: Do 4. Jul 2024, 17:22 Für mich sieht das so als könnte man dies mit Generics nachbilden.

Code: Alles auswählen

generic function GetChild<T>(const Master: TEbmlMaster): T;
begin
  Result :=  T(Master.GetChild(T));
end; 
//call: // MyDocType = specialize GetChild<TDocType>(aMaster);
Ein Master Element hat eine Liste wo andere EBML-Typ-Elemente gespeichert sind.
All die Elemente sind von TEBMLElement abgeleitet und die Liste ist spezialisiert auf den Typ TEBMLElement.

Momentan muss ich das ganze wie folgt aufrufen:

Code: Alles auswählen

begin...
  MyDocType := TDocType(Master.GetChild(TDocType))
end;
Leider funktioniert der Generic Code nicht. Innerhalb der Generic function kann ich nicht mal auf die "const Master" zugreifen, obwohl ich im Funktionskopf den Type für "Master" festlegen kann: TEbmlMaster.

Ich nutze FPC 3.2.2 Lazarus 3.0

Was mache ich falsch?
Bitte gib ein vollständiges, minimales, selbstständiges Beispiel und nicht nur Codeschnipsel.
FPC Compiler Entwickler

hubblec4
Beiträge: 347
Registriert: Sa 25. Jan 2014, 17:50

Re: Generics for Dummies

Beitrag von hubblec4 »

Code: Alles auswählen

generic function GetChild<T>(const Master: TEbmlMaster): T;
begin
  Result :=  T(Master.GetChild(T));
end; 
Das ist der gesamte Generic code, mehr habe ich da nicht bis jetzt.

Fürs testen kann man mein Master gegen eine einfache StringList austauschen.

Code: Alles auswählen

generic function GetChild<T>(const SList: TStringList): T;
begin
  SList. // <--- das geht bereits nicht. An der Stelle nach dem Punkt kann man kein STRG+Leertaste aufrufen.
  Result :=  T(SList.Objects[0]);
end; 

PascalDragon
Beiträge: 962
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: Generics for Dummies

Beitrag von PascalDragon »

Ich sehe hier noch immer keinen kompletten Code. Gib mir bitte etwas, dass ich as-is der fpc.exe vorwerfen kann, da ich keine Lust habe zu raten, was du eventuell noch zusätzlich machst.
FPC Compiler Entwickler

hubblec4
Beiträge: 347
Registriert: Sa 25. Jan 2014, 17:50

Re: Generics for Dummies

Beitrag von hubblec4 »

OK. Ich habs mal in ein mini Programm gepackt.

Code: Alles auswählen

program generic_function;

{$mode objfpc}{$H+}

uses
 {$IFDEF UNIX}
 cthreads,
 {$ENDIF}
 Classes
 { you can add units after this };

generic function GetChild<T>(const SList: TStringList): T;
begin
  //SList. // <--- das geht bereits nicht. An der Stelle nach dem Punkt kann man kein STRG+Leertaste aufrufen.
  Result :=  T(SList.Objects[0]);
end;

var
  SList: TStringList;
  m: TMemoryStream;

begin
  SList := TStringList.Create;
  SList.OwnsObjects := true;
  SList.AddObject('MStream', TMemoryStream.Create);


  m := specialize GetChild<TMemoryStream>(SList);


  SList.Free;
end.

Edit: habe da mal noch eine var angelegt für das auffangen des MemStreams.
Und was mich wundert, es kompiliert und scheint zu klappen.
Ausser eben das man in der generic function nicht auf die Variablen zugreifen kann im Code-Editor.
Aber wenn man es so "hinschreibt" das es alles passt dann scheint der compiler alles richtig zu machen.

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

Re: Generics for Dummies

Beitrag von Mathias »

Ausser eben das man in der generic function nicht auf die Variablen zugreifen kann im Code-Editor.
Aber wenn man es so "hinschreibt" das es alles passt dann scheint der compiler alles richtig zu machen.
Dann ist wohl Lazarus (noch) nicht auf diese Funktion angepasst.
Dies habe ich auch schon mehrmals erlebt.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

hubblec4
Beiträge: 347
Registriert: Sa 25. Jan 2014, 17:50

Re: Generics for Dummies

Beitrag von hubblec4 »

Ok, gut zu wissen. dann muss da Lazarus selbst noch etwas angepasst werden.

Nachdem ich das alles getestet hatte und es zu funktionieren schien, habe ich dann alles in meiner EBML Lib versucht einzubauen.
Dort habe ich wie gesagt statt der TStringList, eine eigene Klasse namens TEbmlMaster.

Wenn ich das nun alles dort so einbaue, dan mekert diesmal der compiler und sagt "Illegal expresion"
Dann habe ich überlegt ob es an meiner Klasse liegen kann. Habe dann mal eine minimal Klasse erstellt und damit gehts auch wieder.

Keine Ahung warum das bei meiner Lib nicht geht.
Die ganze Lib hier zu posten wäre viel zu viel.

EDIT:
Fehler gefunden. Nun klappt es auch in der EBML lib.

hubblec4
Beiträge: 347
Registriert: Sa 25. Jan 2014, 17:50

Re: Generics for Dummies

Beitrag von hubblec4 »

Da nun alles funktioniert möchte ich gerne noch etwas beisteuern was dem ein oder anderen vielleicht hilft.

Das Ziel des ganzen war ja den geschrieben Code etwas zu verkleinern und leserlicher zu machen.

Mit der generic function GetChild spart man sich somit das casten an jeder Stelle des Codes zu schreiben. Dafür muss man nun aber immer das keyword "specialize" schreiben wenn man die generic function aufrufen will.

Daher möchte ich an der Stelle etwas "Werbung" machen für die Code-Macro Funktion.

Code: Alles auswählen

program generic_function;

{$mode objfpc}{$H+}

uses
 {$IFDEF UNIX}
 cthreads,
 {$ENDIF}
 Classes
 { you can add units after this };
 
 {$MACRO ON}  
 {$define GET_CHILD:=specialize GetChild}

generic function GetChild<T>(const SList: TStringList): T;
begin
  Result :=  T(SList.Objects[0]);
end;

var
  SList: TStringList;
  m: TMemoryStream;

begin
  SList := TStringList.Create;
  SList.OwnsObjects := true;
  SList.AddObject('MStream', TMemoryStream.Create);


  //m := specialize GetChild<TMemoryStream>(SList);
    m := GET_CHILD<TMemoryStream>(SList);

  SList.Free;
end.
Vielleicht liesst das ja mal ein FreePascal Entwickler, denn es wäre super genial wenn die Macros auch Paramater verstehen könnten (so wie in c++).
Dann würde das ganze so aussehen.

Code: Alles auswählen

program generic_function;

{$mode objfpc}{$H+}

uses
 {$IFDEF UNIX}
 cthreads,
 {$ENDIF}
 Classes
 { you can add units after this };
 
 {$MACRO ON}  
 {$define GET_CHILD(a,b):=specialize GetChild<a>(b)}

generic function GetChild<T>(const SList: TStringList): T;
begin
  Result :=  T(SList.Objects[0]);
end;

var
  SList: TStringList;
  m: TMemoryStream;

begin
  SList := TStringList.Create;
  SList.OwnsObjects := true;
  SList.AddObject('MStream', TMemoryStream.Create);


  m := GET_CHILD(TMemoryStream, SList);

  SList.Free;
end.

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

Re: Generics for Dummies

Beitrag von Warf »

Oder du benutzt einfach Mode Delphi und ImplicitSpecialization und brauchst auch kein Specialize und oftmals nicht mal mehr <type>:

Code: Alles auswählen

program Project1;

{$mode Delphi}
{$ModeSwitch implicitfunctionspecialization}

procedure GenericWrite<T>(const Val: T);
begin
  WriteLn(Val);
end;

begin
  GenericWrite(42); // Integer
  GenericWrite('42'); // String
  GenericWrite(3.14); // Double
  GenericWrite<Single>(3.14); // Single
end. 
PS: implicitfunctionspecialization braucht Trunk

Antworten