Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Generics <> Templates ? (https://www.delphipraxis.net/95017-generics-templates.html)

Ghostwalker 29. Jun 2007 10:18


Generics <> Templates ?
 
In letzter Zeit stößt man ja öfter auf den Begriff Generics, weshalb ich mich mal versucht habe schlau zu machen. Aber irgendwie wird mir der Unterschied zwischen Generics und Templates (c++-Templates) nicht wirklich klar. Für mich hört sich das an wie zwei Begriffe für die ein und die selbe Sache

Man baut sich eine abstrakte Vorlage, die später (z.B. durch einen Prä-Prozessor) typisiert wird.


Vielleicht kann mir hier mal jemand auf die Sprünge helfen :)

Elvis 29. Jun 2007 10:55

Re: Generics <> Templates ?
 
Zitat:

Zitat von Ghostwalker
Man baut sich eine abstrakte Vorlage, die später (z.B. durch einen Prä-Prozessor) typisiert wird.

Das wären Templates. Generics sind vorkompilierte Klassen, die Jitter oder Slots enthalten, die dann mit Methodenzeigern/Infos der Spezialisierung überschrieben werden.
Vorteil ist, dass du einen Generic direkt kompilieren kannst und somit schon ohne Spezialiserung Fehlermeldungen bekommst.
Nachteil ist, dass du keine Operatoren benutzen kannst. Und auch sonst kannst du nur die Dinge benutzen, die du durch Einschränkungen der Typenparameter festgelegt hast.

Hier mal ein Bleistift (ist allerdings in Chrome, da Delphi noch keine Generics hat):
Delphi-Quellcode:
type
  IDoSomething = public interface
    method DoSomething : String;
  end;


  GenericSample<T> = public static class
    where T is IDoSomething;
  public
    class method Test(instance : T);
  end;

implementation

class method GenericSample<T>.Test(instance : T);
begin
  var miep := instance.DoSomething();
  Console.WriteLine(miep);
end;
Wie du siehst, kann ich die Methode benutzen, weil ich T so eingeschränkt habe, dass es IDoSomething implementieren muss.

DMW 29. Jun 2007 11:57

Re: Generics <> Templates ?
 
Templates sind statisch polymorph, sämtlicher typenspezifischer Code wird also zur Übersetzungszeit generiert. Das führt z.B. bei extensiver Verwendung von Template-Klassen wie der STL zu einem nicht unbeträchtlichen Code-Bloat, da sämtlicher Code für jede Typkombination, mit der das Template instantiiert wird, neu generiert wird.
Da der Code zur Compilierzeit an die Typen angepaßt werden muß, muß er dem Compiler auch zur Verfügung stehen; daher ist es nötig, die gesamte Implementation eines Templates in Headerdateien unterzubringen. (Es gibt dafür zwar das Schlüsselwort export, doch implementiert das praktisch kein Compiler.) Dies ist bei Generics nicht der Fall, da sie zur Laufzeit typisiert werden.

Generics sind allerdings derzeit dadurch im Vorteil, daß man die benötigte Schnittstelle angeben und dadurch undurchschaubaren Fehlermeldungen vorbeugen kann; das wird aber in C++0x mit Concepts auch möglich sein.

Und natürlich gibt es für Generics keine Wertparameter. Damit fällt ein Großteil der Möglichkeiten der Template-Metaprogrammierung weg, durch die in C++ ganz verrückte Dinge möglich werden, z.B. die Wahl eines Typs abhängig davon, ob zwei andere Typen gleich sind:
Code:
typedef IfThenElseType <TypesAreEqual <T, const T>::result,
    RandomIterator <const T, Base, direction, iter>,
    _some_struct_t>::type
        const_iterator_type;

Ghostwalker 29. Jun 2007 14:17

Re: Generics <> Templates ?
 
Ich halte mal fest (hoffentlich hab ich das richtig verstanden):

Templates:

Untypisierte Vorlagen, die vor der Compilierung durch einen Prä-Prozessor typisiert werden,
so das der Compiler zu jeder typisierten Variante entsprechenden Code hat und compilieren kann.

Generics:

Untypisierte "Vorlage", die der Compiler compilieren kann. Diese werden aber erst zur Laufzeit
entsprechend typisiert werden. Was aber auch bedeutet, das eine Typprüfung erst zur Laufzeit
stattfinden kann, da erst da der Typ bekannt ist.


Ist das so richtig ?

@Elvis

Ähm..also...das Beispiel....naja...das könnte man auch heute schon ohne Generics implementieren :)

Flocke 29. Jun 2007 14:57

Re: Generics <> Templates ?
 
Zitat:

Zitat von Ghostwalker
Generics:

Untypisierte "Vorlage", die der Compiler compilieren kann. Diese werden aber erst zur Laufzeit
entsprechend typisiert werden. Was aber auch bedeutet, das eine Typprüfung erst zur Laufzeit
stattfinden kann, da erst da der Typ bekannt ist.


Ist das so richtig ?

Die Typprüfung findet bereits zur Compilezeit statt, allerdings dort wo dein instantiierter Typ deklariert wird - dort kannst du für <T> nämlich nur einen passenden Typen einsetzen.

Ein schönes Beispiel dafür sind typisierte Listen:
Delphi-Quellcode:
type
  TGenericList<T> = class
    where T is TObject
  private
    function GetItem(Index: Integer): T;
    procedure SetItem(Index: Integer; const Value: T);
  public
    procedure Add(AValue: T);
    property Items[Index: Integer]: T read GetItem write SetItem;
  end;
Diese Klasse wir fertig übersetzt in Maschinensprache genau so aussehen wie TObjectList - die Elemente sind TObjects. Sie hat jedoch den Vorteil, dass man Eigenschaften wie "Items" und Methoden wie "Add" wirklich typisieren kann.

Wenn du allerdings einen Typen davon ableitest mit (ich kenne mich mit der Chrome-Syntax nicht aus, kann also falsch sein)
Delphi-Quellcode:
var
  WinControlList = TGenericList<TWinControl>;
dann kann der Compiler schon prüfen, dass nur TWinControl-Objekte darin abgelegt werden. Dann sieht es so aus als ob du den Typ deklariert hättest mit <TWinControl> statt <T>:
Delphi-Quellcode:
type
  // Geht so natürlich nicht
  TGenericList<TWinControl> = class
  private
    function GetItem(Index: Integer): TWinControl;
    procedure SetItem(Index: Integer; const Value: TWinControl);
  public
    procedure Add(AValue: TWinControl);
    property Items[Index: Integer]: TWinControl read GetItem write SetItem;
  end;
... und der Compiler kann dann beim Übersetzen schon einen Typfehler anmeckern, wenn du irgendetwas anderes als ein TWinControl zu deiner Liste hinzufügen willst.

Ghostwalker 29. Jun 2007 16:20

Re: Generics <> Templates ?
 
Aaaaah.....jetzt wirds hell :)

Das heißt man hat die "Funktionalität" von Templates ohne den ganzen zusätzlichen Code, den ein Prä-Prozessor verzapft, und trotzdem die gewohnte pascalsche Typsicherheit :)

OregonGhost 29. Jun 2007 16:36

Re: Generics <> Templates ?
 
Zitat:

Das heißt man hat die "Funktionalität" von Templates ohne den ganzen zusätzlichen Code, den ein Prä-Prozessor verzapft, und trotzdem die gewohnte pascalsche Typsicherheit
Das kann man grundsätzlich so sehen... Allerdings: In C++ ist nicht der Präprozessor für Templates zuständig, sondern der Compiler, und das bereits erwähnte Schlagwort TMP (Template Meta Programming) ist weit mehr als "Generics zur Kompilierzeit". Stark vereinfacht ausgedrückt ermöglicht es, Programme im Compiler auszuführen. Klassisches Beispiel:
Code:
template< int i >
class Factorial{
  public:
      enum {Value = i * Factorial<i - 1>::Value};
};

class Factorial< 1 >{
  public:
      enum {Value = 1};
};
Das ist eine Klasse Factorial, die zur Kompilierzeit (!) eine Fakultät berechnet. Wird ungefähr so verwendet:
Code:
int n = Factorial<10>::Value;
Ob man das braucht, ist eine andere Frage, aber das ist etwas, das man mit Generics allgemein nicht machen kann.

DMW 29. Jun 2007 17:56

Re: Generics <> Templates ?
 
Zitat:

Zitat von Ghostwalker
Das heißt man hat die "Funktionalität" von Templates ohne den ganzen zusätzlichen Code, den ein Prä-Prozessor verzapft, und trotzdem die gewohnte pascalsche Typsicherheit :)

Ungefähr so. Dafür hast du einen gewissen Overhead zur Laufzeit.

Der Code-Bloat läßt sich auch bei Templates vermeiden, indem man Templates intern über typagnostische Klassen implementiert. Leider ist das für die STL nicht so einfach, da diese laut Standard (auch bezüglich der Allokatoren) eine gewaltige Flexibilität bieten muß.


Zitat:

Zitat von OregonGhost
Ob man das braucht, ist eine andere Frage

Zumindest ist es sehr nützlich. Vielleicht nicht gerade für numerische Berechnungen, aber ein praktisches Beispiel hatte ich weiter oben gepostet. Hier etwas ausführlicher:
Code:
template <typename T,
          typename Base,
          int direction = UCL_ITERWRAP_FORWARD,
          typename iter = T*,
          typename tag = std::bidirectional_iterator_tag> // only for derived classes
    class BiDiIterator
        : public std::iterator <tag, T>
{
    typedef std::iterator <tag, T> _iterBase;
private:
    struct _some_struct_t {};
public:
    typedef typename IfThenElseType <TypesAreEqual <T, const T>::result,
        BiDiIterator <const T, Base, direction, iter>,
        _some_struct_t>::type
            const_iterator_type;
    typedef BiDiIterator iterator_type;
    ...
public:
    BiDiIterator (const iterator_type& rhs) : _ptr (rhs._ptr) {}
    BiDiIterator (const const_iterator_type& rhs) : _ptr (rhs._getWrappedIterator ()) {}
   ...
};
Das stammt aus meiner Implementation eines Iterator-Wrappers, der, abhängig davon, ob value_type bereits const ist, sowohl als iterator als auch als const_iterator genutzt werden kann. Allerdings muß ein iterator sowohl aus iterator- als auch aus const_iterator-Objekten konstruierbar sein - und wenn der Iterator dann schon ein const_iterator ist, gibts den Kopierkonstruktor zweimal. Um das zu vermeiden, nimmt der zweite Konstruktor, je nachdem, ob value_type das gleiche wie const value_type ist, entweder einen const_iterator oder eine nichtöffentliche Struktur entgegen.
Weiter kann der Iterator über einen Template-Parameter entweder als (forward-)iterator oder reverse_iterator eingesetzt werden. Auch verwendet er je nachdem, ob der Iteratortyp, den er wrappt, einfach nur ein Zeiger auf den value_type (der immer vorwärts iteriert) oder ein echter reverse_iterator ist, dessen Inkrement- oder Dekrementoperator.

Mach das mal ohne TMP ;)


Alle Zeitangaben in WEZ +1. Es ist jetzt 06:19 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