Unterschied Templates und Generics?



  • Was ist der Unterschied zwischen Templates (C++14) und Generics (Java)?



  • Glorified macro system vs. glorified dynamic casts.



  • nvm



  • adasdda schrieb:

    Was ist der Unterschied zwischen Templates (C++14) und Generics (Java)?

    In Java sind Generics nur für die Typsicherheit da. Ohne Generics müsste man dynamische Casts durchführen (class Dings -> class Object -> class Dings), die zur Laufzeit schief gehen können, wenn man etwas falsch gemacht hat. Mit Java Generics kann man dem Compiler gegenüber mehr ausdrücken, wie man sich das mit den Typen gedacht hat, so dass er zur Übersetzungszeit schon einiges prüfen kann und automatisch die richtigen Casts einfügt. Aus einer Methode einer generischen Klasse bzw generischen Methode wird bei der Übersetzung genau eine Methode, wo die Typinformation, die man über Generics ausgedrückt hat, bei der Übersetzung verloren geht. Dem Byte-Code sieht man das dann nicht mehr an. Die Java-Leute sprechen hier auch von "type erasure".

    In C++ sieht das anders aus. Da definierst Du "Schablonen" für Typen und Funktionen, die dann durch Einsetzen der Template-Parameter "spezialisiert" werden. Aus "pair<T,U>" kann ein "pair<int,int>" werden, was vielleicht 8 Bytes im Speicher belegt oder aber ein "pair<double,double>" was dann wahrscheinlich 16 Bytes im Speicher belegt. Es sind also verschiedene Typen mit typischerweise verschiedenen Repräsentierungen. Aus einer Funktions-Schablone können viele verschiedene Funktionen bei der Übersetzung mit unterschiedlichem Maschinencode werden.

    Das ist das, was audacia mit "glorified dynamic casts vs glorified macro system" meinte.



  • Gibt es etwas, das Generics können, Templates aber nicht?



  • adasdda schrieb:

    Gibt es etwas, das Generics können, Templates aber nicht?

    Mächtigkeit: Templates > Generics



  • Zeus schrieb:

    adasdda schrieb:

    Gibt es etwas, das Generics können, Templates aber nicht?

    Mächtigkeit: Templates >>>>>>>>>>>>>>> Generics

    FTFY 😉



  • dot schrieb:

    Zeus schrieb:

    adasdda schrieb:

    Gibt es etwas, das Generics können, Templates aber nicht?

    Mächtigkeit: Templates >>>>>>>>>>>>>>> Generics

    FTFY 😉

    Wieviele left shift operatoren sind das? XD



  • Zeus schrieb:

    dot schrieb:

    Zeus schrieb:

    adasdda schrieb:

    Gibt es etwas, das Generics können, Templates aber nicht?

    Mächtigkeit: Templates >>>>>>>>>>>>>>> Generics

    FTFY 😉

    Wieviele left shift operatoren sind das? XD

    0 😃

    @adasdda
    Mir wäre nix bekannt was man mit Java Generics anstellen kann, mit C++ templates aber nicht. Also abgesehen von dem ganzen Zeugs was man generell mit Java (auch ohne Generics) machen kann was mit (Standard) C++ nicht geht. Also Garbage Collection, Reflection usw.

    Heisst aber nicht viel, denn ich bin kein Java Experte (genaugenommen nichtmal Java Programmierer, bloss ein Java-ein-bisschen-aus-der-Ferne-Kenner).



  • Können Templates eigentlich co/contravariance?

    MfG SideWinder



  • SideWinder schrieb:

    Können Templates eigentlich co/contravariance?

    Diese Frage ist imho völlig sinnfrei. Im Gegensatz zu Generics sind Templates völlig unabhängig von irgendwelchen Vererbungsbeziehungen. Aber ja, C++ hat kovariante Returntypes und rein prinzipiell könnte man kontravariante Parameter mit Templates erzwingen...


  • Mod

    SideWinder schrieb:

    Können Templates eigentlich co/contravariance?

    Direkte Sprachunterstütuzung: Nein.
    Aber man kann es ihnen eintreiben, wenn der Programmierer dies explizit wünscht. Beispielsweise sind die ganzen Smartpopinter der Standardbibliothek covariant und bilden somit bewusst das natürliche Konvertierungsverhalten von normalen Pointern ab.



  • Generics sind sehr doof. Z.t. weil sie einfach zu spaet eingefuehrt wurden und backward compability die Entwickler eingeschraenkt hat. (Das habe ich unlaengst in einem Talk angesprochen.)

    hustbaer schrieb:

    Also abgesehen von dem ganzen Zeugs was man generell mit Java (auch ohne Generics) machen kann was mit (Standard) C++ nicht geht. Also Garbage Collection, Reflection usw.

    Wo waere GC ueberhaupt sinnvoll? Reicht shared ownership nicht aus?

    Was Reflection angeht: das ist tatsaechlich nervig. Ich glaube wir haben ein paar Paper zu diesem Thema, aber das ganze macht wenig Progress. Ich schau mal ob man was an EWG schicken kann.



  • SideWinder schrieb:

    Können Templates eigentlich co/contravariance?

    Was ist das?



  • Arcoth schrieb:

    hustbaer schrieb:

    Also abgesehen von dem ganzen Zeugs was man generell mit Java (auch ohne Generics) machen kann was mit (Standard) C++ nicht geht. Also Garbage Collection, Reflection usw.

    Wo waere GC ueberhaupt sinnvoll? Reicht shared ownership nicht aus?

    So wie ich das mitbekommen habe gibt es viele lock-free Algorithmen die mit GC recht schön umsetzbar sind aber ohne GC nur unter viel Schmerzen und/oder mit zusätzlichem Overhead. ABA Problem und so.

    Arcoth schrieb:

    Was Reflection angeht: das ist tatsaechlich nervig. Ich glaube wir haben ein paar Paper zu diesem Thema, aber das ganze macht wenig Progress. Ich schau mal ob man was an EWG schicken kann.

    👍





  • hustbaer schrieb:

    Arcoth schrieb:

    hustbaer schrieb:

    Also abgesehen von dem ganzen Zeugs was man generell mit Java (auch ohne Generics) machen kann was mit (Standard) C++ nicht geht. Also Garbage Collection, Reflection usw.

    Wo waere GC ueberhaupt sinnvoll? Reicht shared ownership nicht aus?

    So wie ich das mitbekommen habe gibt es viele lock-free Algorithmen die mit GC recht schön umsetzbar sind aber ohne GC nur unter viel Schmerzen und/oder mit zusätzlichem Overhead. ABA Problem und so.
    👍

    Und warum nimmt man dafür keine GC? Nur weil keiner im Standard ist heißt, das ja nicht daß es keinen gibt.



  • hustbaer schrieb:

    Arcoth schrieb:

    hustbaer schrieb:

    Also abgesehen von dem ganzen Zeugs was man generell mit Java (auch ohne Generics) machen kann was mit (Standard) C++ nicht geht. Also Garbage Collection, Reflection usw.

    Wo waere GC ueberhaupt sinnvoll? Reicht shared ownership nicht aus?

    So wie ich das mitbekommen habe gibt es viele lock-free Algorithmen die mit GC recht schön umsetzbar sind aber ohne GC nur unter viel Schmerzen und/oder mit zusätzlichem Overhead. ABA Problem und so.

    Bin kein Experte auf dem Gebiet aber mir macht es doch immer den Anschein, dass all diese "schönen" Umsetzungen so funktionieren, dass die Existenz eines "GC" mit genau den richtigen Eigenschaften angenommen wird, sodass die ganze eigentliche Komplexität des Problems in diesem "GC" verschwindet. Wenn man's genau nimmt, geht es in der Regel nie wirklich um GC, sondern eigentlich um das Management der Lebensdauer von Objekten, die aus einer Datenstruktur entfernt wurden, aber potentiell noch nebenläufig gerade von jemandem verwendet werden. Das ist eigentlich ein Problem, das völlig orthogonal – wenn nicht sogar antigonal – ist zu GC...

    Wir sagen halt: Es gibt ein Orakel, das hinreichend perfekt, völlig reentrant und ohne Overhead feststellt, welches Objekt genau wann zu zerstören ist, zeigen, dass wir XYZ dann lock-free machen können – wenn man den üblichen Counter-Hack für das ABA Problem als "korrekte Lösung" definiert, versteht sich – und fertig ist die Kiste. Bis jetzt hat mich noch keiner dieser Algos beeindruckt...

    Tyrdal schrieb:

    hustbaer schrieb:

    Arcoth schrieb:

    hustbaer schrieb:

    Also abgesehen von dem ganzen Zeugs was man generell mit Java (auch ohne Generics) machen kann was mit (Standard) C++ nicht geht. Also Garbage Collection, Reflection usw.

    Wo waere GC ueberhaupt sinnvoll? Reicht shared ownership nicht aus?

    So wie ich das mitbekommen habe gibt es viele lock-free Algorithmen die mit GC recht schön umsetzbar sind aber ohne GC nur unter viel Schmerzen und/oder mit zusätzlichem Overhead. ABA Problem und so.
    👍

    Und warum nimmt man dafür keine GC? Nur weil keiner im Standard ist heißt, das ja nicht daß es keinen gibt.

    Der Standard wurde mit C++11 und 14 sogar extra angepasst, um GC Support theoretisch zu erlauben...



  • dot schrieb:

    Bin kein Experte auf dem Gebiet aber mir macht es doch immer den Anschein, dass all diese "schönen" Umsetzungen so funktionieren, dass die Existenz eines "GC" mit genau den richtigen Eigenschaften angenommen wird, sodass die ganze eigentliche Komplexität des Problems in diesem "GC" verschwindet.

    Soweit ich weiss garantiert z.B. die JVM genau diese Annahmen. Und natürlich macht das den GC komplexer. Genau so wie inlining den C++ Compiler komplexer macht (und natürlich jeden anderen Compiler der Inlining macht, also quasi jedes "brauchbare" Compiler-Backend überhaupt, inklusive Hotspot & Co.). In beiden Fällen ist es ein grosser Benefit: man hat ein System das komplex ist, und sehr sehr viele Projekte die dadurch vergleichsweise sehr einfach gut Performance erreichen können.

    dot schrieb:

    Wenn man's genau nimmt, geht es in der Regel nie wirklich um GC, sondern eigentlich um das Management der Lebensdauer von Objekten, die aus einer Datenstruktur entfernt wurden, aber potentiell noch nebenläufig gerade von jemandem verwendet werden. Das ist eigentlich ein Problem, das völlig orthogonal – wenn nicht sogar antigonal – ist zu GC...

    Ich weiss nicht genau wie ich darauf jetzt antworten soll...
    Der GC ist dafür zuständig Müll wegzuräumen. Dafür muss er wissen welche Objekte noch referenziert werden. Und wenn er als Teil eines Systems funktionieren muss welches Threads "hat" und es explizit erlaubt Referenzen ohne extra Synchronisierung einfach so zu verbiegen, dann muss er halt auch damit klarkommen. Und wenn Referenzen in Registern gehalten werden dürfen auch damit und und und. Ich verstehe also nicht wieso das "nicht mit dem GC zu tun" haben soll 😕

    dot schrieb:

    Wir sagen halt: Es gibt ein Orakel, das hinreichend perfekt, völlig reentrant und ohne Overhead feststellt, welches Objekt genau wann zu zerstören ist, zeigen, dass wir XYZ dann lock-free machen können – wenn man den üblichen Counter-Hack für das ABA Problem als "korrekte Lösung" definiert, versteht sich – und fertig ist die Kiste. Bis jetzt hat mich noch keiner dieser Algos beeindruckt...

    Ich bin mir nicht sicher ob ich den Satz so 100% verstehe.

    Aber egal. ABA ist ja auch nicht das einzige Problem.
    Ein weiteres ist z.B. hier beschrieben: https://en.wikipedia.org/wiki/Hazard_pointer
    Und ja, klar kann man Hazard Pointer verwenden. Ist aber auch nicht schön oder einfach, und auch alles andere als gratis.

    Natürlich gibt es auch noch andere Möglichkeiten das zu lösen. z.B. kann man "atomic operations" für shared_ptr verwenden. Wenn man sich dann aber anguckt wie diese implementiert sind, dann wird man sehen dass hier üblicherweise Lock-Pools verwendet werden. Weil man sie auf den meisten CPUs einfach nicht ohne Locks umsetzen kann. Man müsste dazu gleichzeitig:
    - Nen Zeiger aus dem Speicher laden (bzw. bei shared_ptr sogar zwei Zeiger, was aber das kleinste Problem, da man sie direkt nebeneinander ablegen kann -- und bei intrusive_ptr käme man wirklich mit einem Zeiger aus)
    - Prüfen ob er NULL ist, und wenn nicht...
    - Einen über diesen Zeiger erreichbaren Zähler inkrementieren
    Mit Transactional Memory sollte das gehen, aber ohne wüsste ich nicht wie man das lock-free hinbekommen soll.

    Dadurch vernichtet man die "lock-free-heit" der Implementierung und gleichzeitig auch jede Chance darauf dass die Implementierung performancemässig mithalten kann.



  • Tyrdal schrieb:

    Und warum nimmt man dafür keine GC? Nur weil keiner im Standard ist heißt, das ja nicht daß es keinen gibt.

    Der GC muss beweisen können dass ein Objekt nicht mehr referenziert wird bevor er es wegräumt. Vor C++11 kann er das definitiv nicht ohne Support der Implementierung (Compiler, ...). Bzw. meiner Meinung nach kann er es nichtmal mit Support des Compiler.

    Ein Problem ist dabei z.B. dass C++ es erlaubt Zeugs per memcpy, memmove bzw. auch mittels unsigned char* rumzukopieren. Damit kann man völlig standardkonforme Programme bauen, in denen zwischendurch Zustände existieren wo nirgends (Speicher, Register, ...) Bytemuster existieren die einem Zeiger auf Bereich X entsprechen oder auch nur ähnlich sehen. Aber trotzdem danach wieder - auch ganz legan und standardkonform - ein Zeiger auf Bereich X rekonstruiert und verwendet wird. Wenn der GC X dazwischen dann wegräumt ... wäre das blöd.

    Wobei das konstruierte Beispiele sind, die vermutlich in der Realität selten bis gar nicht auftreten werden. Wenn man diese ignoriert, sowie sich auf eine Unzahl von Eigenheiten der C++ Implementierung verlässt und einige mutige Annahmen trifft, dann kann man schon einen mehr oder weniger gut "funktionierenden" GC für C++ basteln. Gibt es ja auch, z.B. den Boehm GC. Würde ich aber nicht verwenden wollen.

    Was genau hier mit C++11/14/1z geändert wurde weiss ich nicht. Ich habe es aber so verstanden dass die neuen Standars es nur der C++ Implementierung einfacher machen selbst einen GC zu integrieren bzw. freiwillig ein Interface für einen externen GC zur Verfügung zu stellen. Das würde heissen dass man immer noch nicht ohne (optionalen) Support des Compilers einen GC dazubasteln kann.


Log in to reply