C++/CLI == schlecht?



  • Da es so schön war, möcht ich noch einen drauf setzen...

    0xdeadbeef schrieb:

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

    Falls es dir noch nicht aufgefallen ist: C++/CLI ist die einzige .NEt-Sprache (ok den assembler mal ausgenommen), mit der sich wirklich alle Features von .Net nutzen lassen. Nennst du sowas einen reinen Adapter zwischen C++ und .NEt? Versuch mal mit C# oder einer anderen Sprachen eine Funktion wie z.B.

    T Convert<T>(System.Object Value)
    {
        return (T)Value;
    }
    

    zu schreiben. Das ist einfach nicht möglich, aber so ist das nunmal.



  • C++/CLI User schrieb:

    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.

    Ich dachte, C++/CLI sollte rückwärts kompatibel zu C++ sein? In C++ funzt der Code nämlich wunderbar. Naja - mal abgesehen von dem Handle-Kram, dens in C++ nicht gibt, und was auch ganz gut so ist. In C++/CLI müsstest du an der Stelle (oder jedesmal, wenn du in C++ einfach nen Pointer übergeben hättest) den selben Code doppelt schreiben, weil handles sich nicht implizit in Pointer umwandeln lassen (das behauptet zumindest der draft).

    C++/CLI User schrieb:

    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.

    Das wollte ich auch nicht behaupten - Zur Not kann man immer noch mit Funktoren in der Gegend rumschmeißen. Das ändert aber nichts daran, dass völlig legaler C++-Code in C++/CLI Blödsinn produzieren kann. Rückwärtskompatibilität ist also eh nicht gegeben.

    C++/CLI User schrieb:

    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.

    Nur, dass ein void-pointer dich nicht weiterbringt, weil C++ eine typsichere Sprache ist. Im Übrigen sind die Handles, von denen ich spreche, nicht meine Idee, den Begriff hab ich aus dem C++/CLI-draft.

    C++/CLI User schrieb:

    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.

    Mit anderen Worten, solange man reines C++ schreibt, hat man keine Probleme. Das widerspricht mir wie? Ganz abgesehen davon läuft der GC innerhalb der VM auch, wenn man ihn nicht braucht.

    C++/CLI User schrieb:

    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 😋

    Dessen bin ich mir bewusst, ich habe damit nur auf einen vorherigen Beitrag geantwortet, der mir einreden wollte, dass in C++ Referenzen nur auf den heap möglich wären. Lesen hilft, weißt du?

    @Optimizer: Über deinen Kram mach ich mir morgen Gedanken. Ich schätze, um auf deine Beiträge zu antworten, sollte ich nicht allzu müde sein 😉



  • C++/CLI User schrieb:

    Versuch mal mit C# oder einer anderen Sprachen eine Funktion wie z.B.

    T Convert<T>(System.Object Value)
    {
        return (T)Value;
    }
    

    zu schreiben. Das ist einfach nicht möglich, aber so ist das nunmal.

    Die Funktion sieht für mich so sinnvoll aus wie #define invert(x) x * -1 (gruß an thedailywtf ;))

    Ich kenne aber C++/CLI nicht. Was macht (T)Value? Wieso geht das in keiner anderen Sprache?



  • 0xdeadbeef schrieb:

    Ich dachte, C++/CLI sollte rückwärts kompatibel zu C++ sein?

    Sorry, aber DAFÜR finde ich keine Worte! Wir hätten uns den Thread wohl ersparen können, wenn du dir mein Posting durchgelesen hättest: EINE NEUE SPRACHE!



  • Heißt das jetzt, auf der einen Seite soll es rückwärts kompatibel zu C++ sein, so dass new sich noch genau so verhalten muss, auf der anderen Seite ist es eine neue Sprache, so dass Rückwärtskompatibilität eigentlich gar nicht wichtig ist? Jetzt entscheide dich doch mal.

    Soll ich noch ein Beispiel geben, warum es Blödsinn ist, new und gcnew zu mischen? Schau dir mal das hier an:

    struct basic_foobar { }
    struct foo : public basic_foobar { };
    struct bar : public basic_foobar { };
    
    struct qux {
      void quux(basic_foobar &p) { }
      foo m_foo;
      bar m_bar;
    };
    
    // ...
    
    qux ^p = gcnew qux;
    p->quux(p->m_foo); // ups - p->m_foo liegt jetzt auf dem CLI-Heap und kann nicht sinnvoll in foo& umgewandelt werden...
    p->quux(p->m_bar);
    

    Damit das läuft, müsstest du qux::quux doppelt implementieren, einmal für basic_foobar& und einmal für basic_foobar%. Das heißt, native C++-Libs einzubinden wird eh schwierig, und den eigenen Code schreibst du doppelt. Oder zumindest mit interior_ptr, aber da musste dann auch wild in der Gegend rumkonvertieren, wenn du die Methode aufrufen willst.

    Vielleicht wird jetzt langsam klar, was ich mit zwei in der Anwendung nahezu identischen Sprachsubsystemen meine, die zu Redundanzen führen.



  • 0xdeadbeef schrieb:

    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.

    Nein, kann man nicht. Sicherlich basiert die Verfügbarkeit von malloc auf der C Kompatibilität, trotzdem ist es ein Bestandteil von C++. Und wenn dir das nicht klart ist, dann lese dir doch bitte Abschnitt C.2 durch. 😉

    0xdeadbeef schrieb:

    groovemaster schrieb:

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

    ...und das widerspricht mir wie?

    Dass du implizierst, dass der Unterschied zwischen gcnew und new völlig trivial ist, im Vergleich zum Unterschied zwischen malloc und new.

    0xdeadbeef schrieb:

    gcnew dagegen unterscheidet sich von new aber nur darin

    Wenn du das anders gemeint hast, dann klär mich diesbzgl. auf.

    0xdeadbeef schrieb:

    Und in welcher Situation ist der GC nicht akzeptabel, wenn man ihn eh im Hintergrund laufen lassen muss?

    ZB Geschwindigkeit? Ich selber hab bisher relativ wenig mit GC gemacht, hab aber gehört, dass GC uU schon mal ein paar ms bzw sec (abhängig von System und Implementierung - nicht speziell auf .NET bezogen) werkelt, um zB Ressourcen frei zu geben oder zu organisieren.

    0xdeadbeef schrieb:

    Du kannst den Kram ja immer noch von Hand löschen

    Und der Vorteil von GC ist für den Clienten dann bitte welcher?

    0xdeadbeef schrieb:

    lies den C++/CLI-draft lieber nochmal

    Im Gegensatz zu dir habe ich nichts behauptet, sondern einfach nur 'ne Frage gestellt.
    Wer lesen kann, ist klar im Vorteil. 😉



  • 0xdeadbeef schrieb:

    Heißt das jetzt, auf der einen Seite soll es rückwärts kompatibel zu C++ sein, so dass new sich noch genau so verhalten muss, auf der anderen Seite ist es eine neue Sprache, so dass Rückwärtskompatibilität eigentlich gar nicht wichtig ist?

    Sorry, aber das ist lächerlich.
    C++ gilt auch als Rückwärtskompatibel zu C, obwohl es dass nicht zu 100% ist und dennoch ist das ein wichtiges Designmerkmal.

    "Heißt das jetzt, auf der einen Seite soll es rückwärts kompatibel zu C sein, so dass malloc sich noch genau so verhalten muss, auf der anderen Seite ist es eine neue Sprache, so dass Rückwärtskompatibilität eigentlich gar nicht wichtig ist?"

    ergibt keinen Sinn, oder?

    Soll ich noch ein Beispiel geben, warum es Blödsinn ist, new und gcnew zu mischen? Schau dir mal das hier an:

    Aus der Luft gegriffen.

    Man merkt klar, dass du dir einige Infos besorgt hast, aber trotzdem 0 Erfahrung mit C++/CLI gesammelt hast.

    Vielleicht wird jetzt langsam klar, was ich mit zwei in der Anwendung nahezu identischen Sprachsubsystemen meine, die zu Redundanzen führen.

    Laut deiner Aussage sind also Referenzen redundant weil es schon Zeiger gibt?
    Und / ist auch redundant, weil du ja auch mit * auskommst.



  • 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.

    Dann mach einen alternativen Vorschlag, der keine "Redundanz" (worin auch immer du die siehst) enthält. Ich will einen GC haben, der ordentlich arbeiten kann, also performant ist. Aber dennoch will ich alles machen können, was ich in normalem ISO-C++ mit Zeigern an tricks machen kann.
    Wenn du dafür eine Lösung hast, dann raus damit.

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

    OK. Ich habe ein Objekt, dessen Laufzeittyp mir nicht bekannt ist (wie auch). Mittles Reflection ermittele ich diesen Typ und instanziere zur Laufzeit ein Generic mit diesem Typ.
    Bau mir ein Equivalent in C++ mit Templates.

    Das wollte ich auch nicht behaupten - Zur Not kann man immer noch mit Funktoren in der Gegend rumschmeißen. Das ändert aber nichts daran, dass völlig legaler C++-Code in C++/CLI Blödsinn produzieren kann. Rückwärtskompatibilität ist also eh nicht gegeben.

    Also wenn ich da nciht durcheinandergekommen bin bezieht sich die aussage auf:

    A ^p = gcnew A;
    A->bar(&A::foo);
    

    In C++ gibt es garkeine Handles. Deswegen funktioniert der Code auch in C++ nicht. Wieso sollte er es dann in C++/CLI? Und vor allem was hat das mit Rückwärtskompatibilität zu tun. Es geht um ein völlig neues Sprachelement.

    Nur, dass ein void-pointer dich nicht weiterbringt, weil C++ eine typsichere Sprache ist.

    Guter Witz. In C++ kannst du Typen nach belieben durcheinander würfeln.

    string foo;
    int & bar = (int&)foo;
    


  • DrGreenthumb schrieb:

    Ich kenne aber C++/CLI nicht. Was macht (T)Value? Wieso geht das in keiner anderen Sprache?

    Das ist eine Funktion, die in einen Datentypen konvertiert, ohne das zum Zeitpunkt der Konvertierung bekannt ist in welchen und trotzdem ohne Boxing und Reflection auskommt. Mit templates geht sowas gar nicht, aber mit Generics geht's, aber nur in C++/CLI. Darum gehts hier aber nicht...

    0xdeadbeef schrieb:

    struct basic_foobar { }
    struct foo : public basic_foobar { };
    struct bar : public basic_foobar { };
    
    struct qux {
      void quux(basic_foobar &p) { }
      foo m_foo;
      bar m_bar;
    };
    
    // ...
    
    qux ^p = gcnew qux;
    p->quux(p->m_foo); // ups - p->m_foo liegt jetzt auf dem CLI-Heap und kann nicht sinnvoll in foo& umgewandelt werden...
    p->quux(p->m_bar);
    

    Du kannst (NOCH) keinen nativen Typen auf dem managed Heap laufen lassen, das frisst der Compiler gar nicht!! Wo ist also das Problem? 🙄
    Wenn es ginge, hättest du natürlich Recht, aber in so einem Fall würde man das auch wieder anders angehen. Lass mich ein kleines Beispiel schreiben:

    #include "stdafx.h"
    #include <iostream>
    
    namespace 
    {
    
        template<typename T>
        static inline void Swap(T %A, T %B)
        {
            T Copy;
            Copy = A;
            A = B;
            B = Copy;
        }
    
    }
    
    int _tmain()
    {
        wchar_t *PtrA = L"Wert von A", 
                *PtrB = L"Wert von B";
        ::Swap(PtrA, PtrB);
        std::wcout << PtrA << std::endl << PtrB << std::endl;
        return 0;
    }
    

    In einer Präsentation von Microsoft (tls310) steht, das % exakt das gleiche Verhalten wie & zeigt, jedoch auch auf den managed Heap verweisen kann. Mit anderen Worten setzt du in diesem Fall einen solchen Verweis und gut ist. 🙄
    Der Grund weshalb du hier nicht einfach ein & verwenden kannst ist doch klar: Die Objekte werden _möglicherweise_ verschoben. Dieser nicht ganz unwichtige Punkt zwingt dich dazu, Referenzen zu unterscheiden.



  • C++/CLI User schrieb:

    DrGreenthumb schrieb:

    Ich kenne aber C++/CLI nicht. Was macht (T)Value? Wieso geht das in keiner anderen Sprache?

    Das ist eine Funktion, die in einen Datentypen konvertiert, ohne das zum Zeitpunkt der Konvertierung bekannt ist in welchen und trotzdem ohne Boxing und Reflection auskommt.

    woher weiß die Funktion, wie sich mein Objekt in einen string konvertiert? Schreibt man dann einen cast-operator oder sowas? Da würde ich jetzt nichts aussergewöhnliches sehen...



  • DrGreenthumb schrieb:

    C++/CLI User schrieb:

    DrGreenthumb schrieb:

    Ich kenne aber C++/CLI nicht. Was macht (T)Value? Wieso geht das in keiner anderen Sprache?

    Das ist eine Funktion, die in einen Datentypen konvertiert, ohne das zum Zeitpunkt der Konvertierung bekannt ist in welchen und trotzdem ohne Boxing und Reflection auskommt.

    woher weiß die Funktion, wie sich mein Objekt in einen string konvertiert? Schreibt man dann einen cast-operator oder sowas? Da würde ich jetzt nichts aussergewöhnliches sehen...

    Hä?! Jetzt versteh ich aber gar nix mehr 😃 . Was hat das mit einem String zu tun? Das Problem ist, dass ich z.B. in C# diesen Code nicht verwenden kann, da ich nicht in T konvertieren darf, bevor T bekannt ist. Bei den Generics ist T aber erst zur Laufzeit bekannt, also frisst der C# Compiler das nicht ohne Boxing oder Late Binding. Da aber ist genau das Problem: Boxing und Late Binding sind relativ lahm und sehen sehr unschön aus. Sobald erst mal etwas "geboxt" ( 😃 ) ist, musst du überall konvertieren ➡ Die Generics sind zu Sau. Mit C++/CLI kann man den Wert einer Variablen vom Typ T setzen, bevor man weiss, um was es sich handelt. Dazu muss man zu Methoden greiffen, welche in C# und anderen Sprachen nicht möglich sind. Aber egal, das ist hier nicht das Thema.



  • Ahh warte: Vielleicht sollte ich noch zur Sicherheit erwähnen, dass es sich bei

    T Convert<T>(System.Object Value)
    {
        return (T)Value;
    }
    

    nicht um C++/CLI- sondern um nicht lauffähigen C#-Pseudo-Code handelt. Gruss ncoh...


Anmelden zum Antworten