C++/CLI == schlecht?



  • jemand meinte heute im irc channel das der standard noch total unausgereift ist und müll wäre. habt ihr euch das schon näher angeguckt? wie ist eure meinung?



  • Nabend,

    ich persoenlich kann mir zwar keine Meinung darueber bilden, da ich es noch
    nicht ausprobiert hab, allerdings finde ich, dass das jeder fuer sich herausfinden
    muss. Dieses Vorgehen beugt auch Flame-Wars vor ;).

    mfg
    v R



  • Ich hab mal in den working draft reingekuckt, der nachher Standard werden soll (zu finden unter http://msdn.microsoft.com/visualc/homepageheadlines/ecma/default.aspx), und meiner Meinung nach ist es das konfuseste Stück Mist, das mir jemals untergekommen ist. Ich würds gern weniger drastisch ausdrücken, aber jede andere Ausdrucksweise wäre gelogen.

    Microsoft war nicht in der Lage, einzusehen, dass C++ von Haus aus einige Sprachfeatures mitliefert, die ihr .net-Framework einfach nicht liefern kann, und versucht jetzt krampfhaft, den Kram zu integrieren. Das Ergebnis: Es wird alles doppelt und dreifach gemacht (templates/generics, new/gcnew, ...), und nichts passt wirklich irgendwie zusammen. Nach dem, was da im working draft stand, sehe ich auch keine wirkliche Möglichkeit, das Konzept noch zu retten, also trifft "Müll" es wohl besser als "unausgereift".

    Aber virtuell Realisticer hat natürlich recht - vertrau nicht blind auf mein Urteil, und kuck lieber selbst in den draft. Mir ist der Kram viel zu seltsam, aber vielleicht ergibt es für dich ja eine Art Sinn.



  • Was mich interessieren würde, wie es mit gcnew und Destruktoren aussieht. Wenn die ihrem .NET Framework nun endlich vernünftige Destruktoren hinzufügen, könnten die das doch auch in C# machen. Was hat diese Welt nur gegen Destruktoren? Wenn ich mir die Java-finalize Lösung angucke, kann ich nur den Kopf schütteln.



  • Die C++/CLI Destruktoren nehmen die Funktion von System.IDisposable.Dispose() ein und werden wie gewohnt am Ende eines Blocks oder beim Auftreten von Exceptions aufgerufen.

    kingruedi schrieb:

    Wenn ich mir die Java-finalize Lösung angucke, kann ich nur den Kopf schütteln.

    Hier liegt ein Misverständnis vor. java.lang.Object.finalize() hat nichts mit einem deterministischem Destruktor zu tun und ist auch nicht als Ersatz dafür gedacht. Als Ersatz für deterministische Destruktoren gibt es finally-Blöcke.
    Für deterministische Destruktion implementiert man Disposable und ruft im finally-Block dispose() auf.

    So läuft es auch bei C#. Bei C++/CLI wie gesagt, werden die C++-Destruktoren intern zu Dispose() umgewandelt und in einen finally-Block eingebettet, so dass du an der Oberfläche das gewohnte Verhalten hast.



  • 0xdeadbeef! Meiner Meinung nach hast du bloss nichts verstanden, denn deine Aussage ist einfach nur inakzeptabel. C++/CLI ist eine ganz neue Sprache, deshalb verstehe ich nicht, was daran falsch ist, das es z.B. ein gcnew gibt? Gleichzeitig soll man aber C++ und C++/CLI innerhalb eines Projektes mischen können, also sind unterschiedliche Schlüsselwörter nötig. Könnte ich ja auch sagen: "Warum hat C++ ein new und kein malloc aus C benutzt?" Aber man hat sich damals halt schon was dabei gedacht.

    Benutze erstmal C++/CLI bevor du hier einfach sagst "Das ist alles sch***!".



  • Wie ich schon sagte, ich habe mir den draft angekuckt, und das Ding ist einfach inkonsistent. C++/CLI ist nicht mal wirklich eine Programmiersprache, es ist ein Adapter zwischen C++ und .net.

    Eine neue Sprache hätte ein konsistentes Sprachkonzept, das bei C++/CLI fehlt - und das zeigt sich insbesondere in den redundanten Strukturen. Welchen Sinn soll es bitte haben, Pointer, Referenzen und Handles zu haben? An der Stelle merkt man sogar noch das WinAPI durch.

    Warum man in einer neuen Sprache neben new noch gcnew einführen muss, ist mir auch schleierhaft - der Vergleich mit malloc hinkt hier gewaltig, da malloc im C++-Standard nicht mal enthalten ist. Die meisten Compiler nehmen es an, aber dafür kann ja die Sprache nicht. Ganz abgesehen davon führt malloc keine Konstruktoren aus. gcnew dagegen unterscheidet sich von new aber nur darin, dass es einen handle anstelle eines pointers zurückgibt, und dass gcnew den Kram dem garbage collector anvertraut. Wenn das ganze jetzt eine neue Sprache wäre, wie du behauptest, warum sollte new den Kram nicht auch dem Garbage collector anvertrauen? Der GC läuft ja sowieso im Hintergrund, also gibt es keine echte Einsparung dadurch, es nicht zu tun. Und wenn man es so täte, wozu bräuchte man dann zwei news?

    Das nächste sind generics und templates. Generics sind eine Art Schmalspurtemplates, dadurch realisiert, dass intern nur void*s (oder ein ähnlicher generischer Datentyp - in Java ist es Object) benutzt und bei der Anwendung entsprechend gecastet wird. Im anwendenden Code sieht das dann aus wie templates - weswegen templates generics eigentlich überflüssig machen - aber aus offensichtlichen Gründen nicht so mächtig. Trotzdem wird in C++/CLI beides benutzt, was wieder eine unnnötige, redundante Sprachstruktur ist.

    C++/CLI strotzt nur so vor Redundanzen, und Konsistenz ist nirgendwo zu sehen. Wenn das kein Mist ist, was ist dann Mist?



  • 0xdeadbeef: MS will die Entwickler eh auf C# bringen. Von daher passt das alles schon.



  • Was solls.
    Andere Betriebssysteme haben auch schöne IDEs.



  • Welchen Sinn soll es bitte haben, Pointer, Referenzen und Handles zu haben? An der Stelle merkt man sogar noch das WinAPI durch.

    Mit Zeigern kann man ganz viel Lowlevel-Kram machen. Mit Referenzen kannst du z.B. einen operator[] schreiben, der sich so verhält, wie der der eingebauten Arrays. Mit Handles hast du etwas GC-taugliches.

    Warum man in einer neuen Sprache neben new noch gcnew einführen muss, ist mir auch schleierhaft

    Jenachdem, ob ich deterministisch ein Objekt zerstören will oder es egal ist kann ich zwischen new und gcnew wählen. In der Regel kann man natürlich das bequemere gcnew verwenden.

    Wenn das ganze jetzt eine neue Sprache wäre, wie du behauptest, warum sollte new den Kram nicht auch dem Garbage collector anvertrauen?

    Dadurch wäre man nicht mehr abwärtskompatibel zum normalen C++.

    Das nächste sind generics und templates. Generics sind eine Art Schmalspurtemplates, dadurch realisiert, dass intern nur void*s (oder ein ähnlicher generischer Datentyp - in Java ist es Object) benutzt und bei der Anwendung entsprechend gecastet wird. Im anwendenden Code sieht das dann aus wie templates - weswegen templates generics eigentlich überflüssig machen - aber aus offensichtlichen Gründen nicht so mächtig. Trotzdem wird in C++/CLI beides benutzt, was wieder eine unnnötige, redundante Sprachstruktur ist.

    Keines Falls.
    Möglicherweise willst du eine Bibliothek erstellen, die von allen .Net-Sprachen verwendbar sein soll. Dann sind Gerenics die einzige Möglichkeit.
    Außerdem bieten Generics Möglichkeiten, die weit über das hinausgehen, was Templates können, z.B. im Bereich Reflection. Du kannst Generics auch zur Laufzeit instanziieren. Tempaltes hingegen werden komplett zur Compilezeit aufgelöst.
    Sie ergänzen sich also wunderbar.



  • Wenn C++/CLI tatsächlich eine neue Sprache wäre, müsste man nicht komplett "abwärts"kompatibel zu C++ sein.

    Was die generics angeht, das unterstreicht wunderbar meinen Punkt, dass C++/CLI keine Programmiersprache, sondern ein Adapter zwischen C++ und .net ist.

    Oh, und @Artchi noch ein Punkt, den ich vorhin vergessen habe - C++ und C++/CLI in einem Projekt verwenden zu können, bedeutet nicht, dass die selben Schlüsselwörter sich gleich verhalten müssen. Ich kann auch so locker in dem selben Projekt C, C++, Perl, Pascal und *hier bitte beliebige Sprache einsetzen* mischen. Das nennt sich "language binding" - oder hast du noch nie in einem C++-Projekt eine C-Library benutzt?



  • 0xdeadbeef schrieb:

    der Vergleich mit malloc hinkt hier gewaltig

    Nö, ist schon ok so.

    0xdeadbeef schrieb:

    da malloc im C++-Standard nicht mal enthalten ist.

    Bitte? Konsultieren sie umgehend die Standard Ausgabe ihres Vertrauens (20.4.6). 🙄

    0xdeadbeef schrieb:

    Ganz abgesehen davon führt malloc keine Konstruktoren aus. gcnew dagegen unterscheidet sich von new aber nur darin, dass es einen handle anstelle eines pointers zurückgibt, und dass gcnew den Kram dem garbage collector anvertraut.

    Das ist aber nicht weniger trivial als der Unterschied zwischen malloc und new.

    0xdeadbeef schrieb:

    Wenn das ganze jetzt eine neue Sprache wäre, wie du behauptest, warum sollte new den Kram nicht auch dem Garbage collector anvertrauen?

    Weil man so nativen und .NET Code mischen kann, und uU nur in bestimmten Situationen GC akzeptabel ist? 🙄

    0xdeadbeef schrieb:

    Wenn das kein Mist ist, was ist dann Mist?

    Deine Argumentation? 😕



  • 0xdeadbeef schrieb:

    Welchen Sinn soll es bitte haben, Pointer, Referenzen und Handles zu haben? An der Stelle merkt man sogar noch das WinAPI durch.

    Pointer sind Pointer, Referenzen sind Verweise auf Objekte, die auf dem managed Heap liegen. Und Handles sind was völlig anderes, was auch gar nichts mit der Sprache an sich zu tun hat.

    gcnew dagegen unterscheidet sich von new aber nur darin, dass es einen handle anstelle eines pointers zurückgibt, und dass gcnew den Kram dem garbage collector anvertraut.

    Ach, das ist ja auch fast kein Unterschied.

    Wenn das ganze jetzt eine neue Sprache wäre, wie du behauptest, warum sollte new den Kram nicht auch dem Garbage collector anvertrauen?

    Das machen beispielsweise Java und C#. C++/CLI unterscheidet sich darin, indem es dem Programmierer die Wahl lässt. Wenn du diese Wahlmöglichkeit nicht brauchst, dann vergiss new und benutz nur noch gcnew.

    Der GC läuft ja sowieso im Hintergrund, also gibt es keine echte Einsparung dadurch, es nicht zu tun. Und wenn man es so täte, wozu bräuchte man dann zwei news?

    Was für ein Unsinn. 🙄 Was soll denn am GC "laufen", wenn man gcnew nicht benutzt? Weißt du überhaupt, wie Garbage Collectoren funktionieren?

    Das nächste sind generics und templates. Generics sind eine Art Schmalspurtemplates

    Nein. Generics haben gegenüber Templates einige Vorteile, insgesamt weniger Möglichkeiten und werden sowohl völlig anders verwendet als auch völlig anders implementiert.

    dadurch realisiert, dass intern nur void*s (oder ein ähnlicher generischer Datentyp - in Java ist es Object) benutzt und bei der Anwendung entsprechend gecastet wird.

    Nope. RTFM.



  • groovemaster schrieb:

    Bitte? Konsultieren sie umgehend die Standard Ausgabe ihres Vertrauens (20.4.6). 🙄

    Der Abschnitt 20.4.6 ist mit "C Library" überschrieben. OK, es steht drin, aber es ist ledliglich der C-compatibility layer, über den wir da reden. Ich sehe ein, dass man sich darüber streiten kann, aber es ist jedenfalls keine Kernkomponente von C++.

    groovemaster schrieb:

    Das ist aber nicht weniger trivial als der Unterschied zwischen malloc und new.

    ...und das widerspricht mir wie?

    groovemaster schrieb:

    Weil man so nativen und .NET Code mischen kann, und uU nur in bestimmten Situationen GC akzeptabel ist? 🙄

    new und gcnew treten erst bei der Instanziierung in Aktion, sind also für die Interoperabilität von nativem und .net-Code unerheblich. Und in welcher Situation ist der GC nicht akzeptabel, wenn man ihn eh im Hintergrund laufen lassen muss? Du kannst den Kram ja immer noch von Hand löschen (lies den C++/CLI-draft lieber nochmal).



  • Optimizer schrieb:

    Pointer sind Pointer, Referenzen sind Verweise auf Objekte, die auf dem managed Heap liegen. Und Handles sind was völlig anderes, was auch gar nichts mit der Sprache an sich zu tun hat.

    Referenzen sind nicht zwingend Verweise auf Objekte auf dem Heap. Jedenfalls nicht in C++, da ist das hier:

    int x, &r = x;
    

    durchaus gültiger Code. Was handles und tracked references angeht, handles sind pointer auf Objekte auf dem CLI-Heap, tracked references die entsprechenden Referenzen. Warum Objekte sich auf dem Heap hin- und herbewegen, entzieht sich wieder meinem Verständnis, aber CLI macht es scheinbar so. Jedenfalls laufen da zwei nahezu identische Systeme parallel, was in meinem Verständnis kein sauberes Sprachdesign ist.

    Optimizer schrieb:

    Ach, das ist ja auch fast kein Unterschied.

    Es wäre keiner, wenn C++/CLI eine konsistent durchdesignte Sprache und kein bloßer Adapter wäre.

    Optimizer schrieb:

    Das machen beispielsweise Java und C#. C++/CLI unterscheidet sich darin, indem es dem Programmierer die Wahl lässt. Wenn du diese Wahlmöglichkeit nicht brauchst, dann vergiss new und benutz nur noch gcnew.

    Der Punkt ist, dass redundante Sprachstrukturen verwirrend sind. Gerade ein Anfänger wird dir kaum sagen können, was an

    foo ^p = new foo;
    

    falsch ist.

    Optimizer schrieb:

    Was für ein Unsinn. 🙄 Was soll denn am GC "laufen", wenn man gcnew nicht benutzt? Weißt du überhaupt, wie Garbage Collectoren funktionieren?

    Der GC läuft in der CLI-VM, und die läuft bei CLI-Programmen wohl immer, oder? Und ja, die Prinzipien eines garbage collectors sind mir vertraut.

    Optimizer schrieb:

    Nein. Generics haben gegenüber Templates einige Vorteile, insgesamt weniger Möglichkeiten und werden sowohl völlig anders verwendet als auch völlig anders implementiert.

    Dann zeig mir ein Stück code, das mit generics funzt, aber mit templates nicht.

    Optimizer schrieb:

    Nope. RTFM.

    Ah, doch, so funzt der Kram intern. generics werden im Gegensatz zu templates nicht konkretisiert, deswegen kann man sie auch einfacher in bytecode packen.



  • Das artet doch genauso wie 5 andere Threads pro Woche in sinnlosem geflame aus.

    0xdeadbeef: Wenn du C++/CLI oder C# nicht magst, dann es das dein Ding. Du hast recht. In vielen Bereichen ist C++/CLI mehr eine Art Layer, man kann ja auch nicht von heute auf morgen C++ aus der Windows Welt rausschmeißen.

    Warum hat MS so viel Geld in .NET gesteckt? Haben die sich etwas dabei gedacht, dass es mehr Ähnlichkeiten mit Java hat als mit C++? Wird C# ähnlich erfolgreich sein wie Java?

    Warum hat MS nicht lieber dieses Budget in ein besseres C++ Framework gesteckt? Also nachdenken - warum mehr Geld ausgeben und eine neue Sprache schaffen, wenn es doch C++ gibt?

    Die Zukunft für alle nicht Low-Level Programme wird auf Dauer C# und Java sein.



  • Oh, und noch eins - komplexere Anwendungen werden durch diese Unterscheidung von handles und pointern quasi unmöglich. Nimm zum Beispiel:

    struct A {
      virtual void foo() { }
      void bar(void (A::*f)()) { (this->*f)(); }
    };
    
    // ...
    
    A ^p = gcnew A;
    A->bar(&A::foo);
    

    ...nur um ein Beispiel zu nennen.



  • Referenzen sind nicht zwingend Verweise auf Objekte auf dem Heap. Jedenfalls nicht in C++, da ist das hier:
    C/C++ Code:
    int x, &r = x;

    Meine Güte. Wenn du die Referenzen von C++/CLI anprangerst, dann darf ich davon ausgehen, dass du nicht sowas meinst, oder? Die Verweise auf Objekte auf dem verwalteten Heap, die du scheinbar Handles nennst, sind nun mal was anderes als Pointer und man braucht sie, weil genau wegen den Pointern Standard C++ keinen wirklich guten GC bekommen kann.

    0xdeadbeef schrieb:

    Warum Objekte sich auf dem Heap hin- und herbewegen, entzieht sich wieder meinem Verständnis,

    Gut, dann kann ich schon mal davon ausgehen, dass du nicht weißt, wie ein GC arbeitet. Springen wir gleich mal zu diesem Punkt hin.

    Optimizer schrieb:

    Was für ein Unsinn. 🙄 Was soll denn am GC "laufen", wenn man gcnew nicht benutzt? Weißt du überhaupt, wie Garbage Collectoren funktionieren?

    Der GC läuft in der CLI-VM, und die läuft bei CLI-Programmen wohl immer, oder? Und ja, die Prinzipien eines garbage collectors sind mir vertraut.

    Hmmmm glaube ich dir nicht. Wie auch immer, es "läuft" gar nichts, wenn du keine Objekte auf dem managed Heap anlegst, gibts nichts zum collecten, so einfach ist das. Und wenn man es schon nicht weiß, sollte man auch nicht irgendwelche Annahmen machen, wie die, das ständig irgendwas "läuft", die dann auf keiner Grundlage beruhen.

    Optimizer schrieb:

    Das machen beispielsweise Java und C#. C++/CLI unterscheidet sich darin, indem es dem Programmierer die Wahl lässt. Wenn du diese Wahlmöglichkeit nicht brauchst, dann vergiss new und benutz nur noch gcnew.

    Der Punkt ist, dass redundante Sprachstrukturen verwirrend sind. Gerade ein Anfänger wird dir kaum sagen können, was an

    foo ^p = new foo;
    

    falsch ist.

    Na und? C++/CLI ist auch keine Anfängersprache, genausowenig wie C++ eine ist. Man hat auch beim Design von Java und C# nicht auf Anfänger Rücksicht genommen, sondern beobachtet, was bei anderen Sprachen vielleicht falsch läuft und was selbst Profis mit jahrelanger Erfahrung ärgert. Und ich sage dir nochmal, dass da nichts redundant ist. Die Verweise mit Typ^ gibt es, weil die normalen Pointer vernünftige Garbage Collection verhindern. Und so ganz nebenbei funktionieren sie auch völlig anders, weil sie nicht direkt auf das Zielobjekt zeigen, sondern das Zielobjekt nur über zwei Indirektionen ansprechen lassen, sonst könnte man es dem GC fast nicht ermöglichen, Objekte zu verschieben.

    Optimizer schrieb:

    Nein. Generics haben gegenüber Templates einige Vorteile, insgesamt weniger Möglichkeiten und werden sowohl völlig anders verwendet als auch völlig anders implementiert.

    Dann zeig mir ein Stück code, das mit generics funzt, aber mit templates nicht.

    Nein, ich erkläre dir jetzt nicht den Unterschied. Und nur dadurch, dass generische Typen überhaupt existieren (d.h. sich in CIL Code manifestieren), was Templates nicht tun, kann man sie in anderen .Net Sprachen verwenden oder in eine DLL packen und dann mit einem Typen verwenden, den man vorher nicht gekannt hat.

    Optimizer schrieb:

    Ah, doch, so funzt der Kram intern. generics werden im Gegensatz zu templates nicht konkretisiert, deswegen kann man sie auch einfacher in bytecode packen.

    Du hast vorhin was von casten geschrieben und das ist Unsinn. Die Generics werden zur JIT-Compilierzeit instanziert und funktionieren prinzipiell dann wie Templates. Referenztyen teilen sich den native Code, weil der Elementtyp dort einfach <Referenz> ist. Da ist nirgendwo ein cast.



  • Guten Abend!!1

    0xdeadbeef schrieb:

    Oh, und noch eins - komplexere Anwendungen werden durch diese Unterscheidung von handles und pointern quasi unmöglich. Nimm zum Beispiel:

    struct A {
      virtual void foo() { }
      void bar(void (A::*f)()) { (this->*f)(); }
    };
    
    // ...
    
    A ^p = gcnew A;
    A->bar(&A::foo);
    

    ...nur um ein Beispiel zu nennen.

    Ich will hier nicht irgendwie nerven, aber weisst du überhaupt, was du da geschrieben hast? Wenn du dich mal richtig mit der Architektur von .Net und C++/CLI Syntax beschäftigt hättest (und nein: ICH hab das gemacht, seit es die Beta zum Download gibt 😃 ), dann wüsstest du auch, weshalb sich dieser Code nicht kompilieren lässt. An dieser Stelle würde ein erfahrener .Net Programmierer ein System::Delegate verwenden und nicht einen "normalen" Member-Functionpointer. Zu behaupten, dass diese Funktionalität mit .Net 2.0 unmöglich ist, ist einfach... ich weiss nicht. Ausserdem hat dein Beispiel gar nichts mit der Unterscheidung von "Handles" und Pointern zu tun, da dein Code gar keinen Sinn ergibt. "Handle" ist als Bezeichnung für einen verwalteten Zeiger vielleicht eine dumme Bezeichnung, aber wenigstens sind so Verwechslungen ausgeschlossen (was im Draft sehr wichtig war). Ich für mich rede weiterhin von Zeigern, egal was es nun ist (hauptsache es "zeigt" irgendwo hin und verhält sich auch so). Im übrigen lässt sich ein "Handle" in deinem Sinn auch in einen void-Zeiger un natürlich wieder zurück konvertieren.

    Der GC läuft in der CLI-VM, und die läuft bei CLI-Programmen wohl immer, oder? Und ja, die Prinzipien eines garbage collectors sind mir vertraut.

    Ich würde wie bereits oben erwähnt meinen, der GC würde nur dann gebraucht, wenn man den managed Heap benutzt. Dies ist solange man System::ValueType-Typen, unmanaged Zeugs und natürlich verwaltete Dinge auf dem Stack benutzt nicht der Fall. Ausserdem hast du mit C++/CLI die volle Kontrolle über den Eintritt/Austritt in die CLR, da du mit unmanaged Code starten und dann wechseln kannst.

    Referenzen sind nicht zwingend Verweise auf Objekte auf dem Heap. Jedenfalls nicht in C++, da ist das hier:
    C/C++ Code:

    int x, &r = x;
    

    Ahhh!11 😃
    Du hast echt keine Ahnung was? Dieser Code lässt sich ja auch mit C++/CLI kompilieren, es ist die unmanaged Variante von

    int x, %r = x;
    

    DAS ist eine .Net-Reference und nix anderes 🙄 . Aber ich sehe schon, es bringt nix 😋



  • C++/CLI User == Optimizer !!!!!!111 😃


Anmelden zum Antworten