Kann man jedes C++-Programm cast-frei schreiben?



  • Man wird wohl jedes Problem ohne casts Lösen können. Aber oft ist das bestimmt nicht empfehlenswert.



  • camper schrieb:

    warum sollte man das wollen?

    weil casts immer einen unsicherheitsfaktor darstellen. Natürlich spricht nichts gegen implizite casts, aber sobald man nachhelfen muss, muss man sich fragen, ob bislang alles richtig gelaufen ist(es gibt zwar fälle bei denen man um einen Cast nicht herumkommt, aber die kann man auch an einer Hand abzählen).

    aber ich stell mal die gegenfrage: warum sollte man das nicht wollen?


  • Mod

    otze schrieb:

    camper schrieb:

    warum sollte man das wollen?

    weil casts immer einen unsicherheitsfaktor darstellen.

    damit kann ich nichts anfangen. jedes sprachmittel ist potentiell ein unsicher heitsfaktor, wenn man nicht weiß, was man tut.[quote]

    aber ich stell mal die gegenfrage: warum sollte man das nicht wollen?

    warum sollte man sich von vornherein sprachmittelen verschließen, die nicht prinzipiell mit overhead verbunden sind (das ist ja etwas, das in einzelfällen gegen RTTI/exceptions und z.T. templates spricht)? insbesondere dem function-style-cast entkommt man nur schwer, wenn man lesbaren und einigermaßen kompakten code schreiben will, schon ein simples

    std::string("foo")
    

    ist ja so etwas.



  • ich meinte jetzt nur C-cast und *_cast



  • OP schrieb:

    ich meinte jetzt nur C-cast und *_cast

    Da man jeden C-Cast als function-style-cast oder als *_cast schreiben kann: ja



  • Lolz schrieb:

    OP schrieb:

    ich meinte jetzt nur C-cast und *_cast

    Da man jeden C-Cast als function-style-cast oder als *_cast schreiben kann: ja

    echt? wie kann ich function-style einen std::string auf eine std::map casten?



  • OP schrieb:

    Lolz schrieb:

    OP schrieb:

    ich meinte jetzt nur C-cast und *_cast

    Da man jeden C-Cast als function-style-cast oder als *_cast schreiben kann: ja

    echt? wie kann ich function-style einen std::string auf eine std::map casten?

    Wie geht das denn mit C-Cast? So jedenfalls nicht:

    std::string s;
      std::map<int, int> m;
      m = (std::map<int, int>)s;
    

    Mir fällt dennoch ein Beispiel ein, wo man mit function-style casts nicht weiter kommt. Und zwar den cast zu einen Zeiger auf eine Funktion. Dieser ist prinzipiell nicht erlaubt und nur mit C-style-casts zu lösen. Das ist mir bei der Verwendung von dlsym(3) aufgefallen.

    Aber der caster hat schon recht, wenn er casts vermeiden möchte. In der Regel sind explizite casts in welcher Form auch immer ein Indiz für schlechtes Design. Ich Betone: In der Regel! Es gibt natürlich Fälle, wo das Sinnvoll ist, aber die sind imho eher selten.

    Tntnet


  • Mod

    tntnet schrieb:

    Mir fällt dennoch ein Beispiel ein, wo man mit function-style casts nicht weiter kommt. Und zwar den cast zu einen Zeiger auf eine Funktion. Dieser ist prinzipiell nicht erlaubt und nur mit C-style-casts zu lösen. Das ist mir bei der Verwendung von dlsym(3) aufgefallen.

    typedef 😉



  • OP schrieb:

    Lolz schrieb:

    OP schrieb:

    ich meinte jetzt nur C-cast und *_cast

    Da man jeden C-Cast als function-style-cast oder als *_cast schreiben kann: ja

    echt? wie kann ich function-style einen std::string auf eine std::map casten?

    Wer würde sowas Krankes machen wollen? Ganz abgesehen davon dass das total bescheuert wäre ist es auch noch extrem gefährlich.

    grüße



  • camper schrieb:

    tntnet schrieb:

    Mir fällt dennoch ein Beispiel ein, wo man mit function-style casts nicht weiter kommt. Und zwar den cast zu einen Zeiger auf eine Funktion. Dieser ist prinzipiell nicht erlaubt und nur mit C-style-casts zu lösen. Das ist mir bei der Verwendung von dlsym(3) aufgefallen.

    typedef 😉

    Hilft auch nicht. Und der Compiler gcc 3.3.5 sagt auch warum.

    typedef void (*function_type)();
    
    int main()
    {
      void* sym = 0;
      function_type fn1 = static_cast<function_type>(sym);
      function_type fn2 = reinterpret_cast<function_type>(sym);
      function_type fn3 = dynamic_cast<function_type>(sym);
      function_type fn4 = (function_type)sym;
    }
    

    Übersetzt mit "g++ -c ttt.cpp" ergibt:

    ttt.cpp: In function `int main()':
    ttt.cpp:7: error: invalid static_cast from type `void*' to type `void (*)()'
    ttt.cpp:8: error: ISO C++ forbids casting between pointer-to-function and
       pointer-to-object
    ttt.cpp:9: error: cannot dynamic_cast `sym' (of type `void*') to type `void
       (*)()' (target is not pointer or reference to class)
    

    Die Zeile 8 mit dem reinterpret_cast gibt den entscheidenden Hinweis: ISO C++ verbietet das grundsätzlich. Erst der C-Style-cast erlaubt das.

    Der Grund ist, daß das nicht portabel ist. Es gibt Systeme, bei denen Pointer auf Daten was grundsätzlich verschiedenes sind als Pointer auf Code. Da kann der Pointer den selben Wert haben, zeigen aber auf verschiedenen physikalischen Speicher.

    Tntnet



  • Leider fehlt in dem Beispiel nun ausgerechnet der Function-Style-Cast (um den es hier ging)...



  • Was meinst Du mit function-style cast? Etwa:

    function_type fn5 = function_type(sym);
    

    Das ist doch gewissermassen nur eine andere Schreibweise für den C-style-cast.

    Das funktioniert schon, aber das ist ja wirklich übler stil. Der versteckt den Cast bis zur Unkenntlichkeit.

    Tntnet



  • OP schrieb:

    Lolz schrieb:

    OP schrieb:

    ich meinte jetzt nur C-cast und *_cast

    Da man jeden C-Cast als function-style-cast oder als *_cast schreiben kann: ja

    echt? wie kann ich function-style einen std::string auf eine std::map casten?

    Dir ist die Bedeutung des Wortes "oder" schon klar?



  • tntnet schrieb:

    Was meinst Du mit function-style cast? Etwa:

    function_type fn5 = function_type(sym);
    

    Das ist doch gewissermassen nur eine andere Schreibweise für den C-style-cast.

    Na da haben wir ja prima aneinander vorbei geredet 😉

    Ja, das nennt man landläufig function-style cast. Und ich denke eigentlich nicht dass es den Cast versteckt, denn man kann IMHO ganz gut erkennen, was gemeint ist:

    std::string( "Hallo" ) // konstruiere einen std::string aus dem C-String "hallo"
    int( 13.4 ) // Wandle 13.4 in ein Integer um
    

    finde ich wesentlich schöner als

    (std::string) "Hallo" // oder gar
    static_cast<int>( 13.4 ) // irgh
    

    In dem Zusammenhang: Ein Function-Style-Cast auf einen Zeiger geht generell nicht, da dieser Cast für zusammengesetzte Typen nicht definiert ist, im Sinne von

    const char* x = "Hallo, welt";
    const unsigned char*( x )
    

  • Mod

    tntnet schrieb:

    camper schrieb:

    tntnet schrieb:

    Mir fällt dennoch ein Beispiel ein, wo man mit function-style casts nicht weiter kommt. Und zwar den cast zu einen Zeiger auf eine Funktion. Dieser ist prinzipiell nicht erlaubt und nur mit C-style-casts zu lösen. Das ist mir bei der Verwendung von dlsym(3) aufgefallen.

    typedef 😉

    Hilft auch nicht. Und der Compiler gcc 3.3.5 sagt auch warum.

    typedef void (*function_type)();
    
    int main()
    {
      void* sym = 0;
      function_type fn1 = static_cast<function_type>(sym);
      function_type fn2 = reinterpret_cast<function_type>(sym);
      function_type fn3 = dynamic_cast<function_type>(sym);
      function_type fn4 = (function_type)sym;
    }
    

    Übersetzt mit "g++ -c ttt.cpp" ergibt:

    ttt.cpp: In function `int main()':
    ttt.cpp:7: error: invalid static_cast from type `void*' to type `void (*)()'
    ttt.cpp:8: error: ISO C++ forbids casting between pointer-to-function and
       pointer-to-object
    ttt.cpp:9: error: cannot dynamic_cast `sym' (of type `void*') to type `void
       (*)()' (target is not pointer or reference to class)
    

    Die Zeile 8 mit dem reinterpret_cast gibt den entscheidenden Hinweis: ISO C++ verbietet das grundsätzlich. Erst der C-Style-cast erlaubt das.

    Der Grund ist, daß das nicht portabel ist. Es gibt Systeme, bei denen Pointer auf Daten was grundsätzlich verschiedenes sind als Pointer auf Code. Da kann der Pointer den selben Wert haben, zeigen aber auf verschiedenen physikalischen Speicher.

    Tntnet

    ah. jetzt wird klar, was du meinst. du täuscht dich allerdings hinsichtlich des casts von objekt-pointern in funktionspointer. das ist in C++ grundsätzlich nicht erlaubt, auch nicht mit C-style casts (es wird möglicherweise im nächsten standard conditional-defined sein). wenn du den compiler als beleg heranziehst, benutz doch bitte einen modus ohne spracherweiterungen (-ansi -std=c++98). in C99 ist dieser cast durchaus erlaubt und g++ lässt diese erweiterung offenbar auch für c++ zu, ohne aber alle syntaktisch denkbaren formen zu ermöglichen. ein cast von funktionspointern in andere funktionspointer ist dagegen auch in C++ möglich.

    LordJaxom schrieb:

    In dem Zusammenhang: Ein Function-Style-Cast auf einen Zeiger geht generell nicht, da dieser Cast für zusammengesetzte Typen nicht definiert ist, im Sinne von

    const char* x = "Hallo, welt";
    const unsigned char*( x )
    

    das geht durchaus, denn ein pointertyp kann mittels typedef durchaus auch als einfache id auftauchen. ein implementation, die std::vector iteratoren durch einfache pointer implementiert, würde zum beispiel ein konstrukt der form

    std::vector<int>::iterator foo = std::vector<int>::iterator(1000);
    

    zulassen.



  • otze schrieb:

    camper schrieb:

    warum sollte man das wollen?

    [...]Natürlich spricht nichts gegen implizite casts[...]

    Bis auf die Tatsache, dass es soetwas nicht gibt 😉
    casts sind *explizite* Typkonvertierungen und können somit schwer implizit passieren.



  • @camper:
    Sorry, unglücklich ausgedrückt. Meinte natürlich "ohne typedef" (im Bezug auf Dein Posting "typedef ;)")...



  • [quote="camper"][quote="tntnet"]

    camper schrieb:

    tntnet schrieb:

    ...
    Der Grund ist, daß das nicht portabel ist. Es gibt Systeme, bei denen Pointer auf Daten was grundsätzlich verschiedenes sind als Pointer auf Code. Da kann der Pointer den selben Wert haben, zeigen aber auf verschiedenen physikalischen Speicher.

    Tntnet

    ah. jetzt wird klar, was du meinst. du täuscht dich allerdings hinsichtlich des casts von objekt-pointern in funktionspointer. das ist in C++ grundsätzlich nicht erlaubt, auch nicht mit C-style casts (es wird möglicherweise im nächsten standard conditional-defined sein). wenn du den compiler als beleg heranziehst, benutz doch bitte einen modus ohne spracherweiterungen (-ansi -std=c++98). in C99 ist dieser cast durchaus erlaubt und g++ lässt diese erweiterung offenbar auch für c++ zu, ohne aber alle syntaktisch denkbaren formen zu ermöglichen. ein cast von funktionspointern in andere funktionspointer ist dagegen auch in C++ möglich.

    Ich weiß sehr wohl, daß der C-style cast ein Hack ist. In C++ sollte man grundsätzlich die neuen casts verwenden. Ich habe ja auch begründet, warum ein Cast eines Zeigers auf Daten auf einen Zeiger auf Funktion eigentlich keinen Sinn macht.

    Ich will auch nicht den Compiler als Beweiß für irgenwelche Standards verwenden. Da hilft auch kein "-ansi -std=c++98". Der C-style cast funktioniert mit gcc, was aber natürlich nicht heißt, daß das erlaubt ist, da er ja noch nichtmal technisch möglich sein muß.

    Wenn ich allerdings eine shared library mit dlopen öffne und mit dlsym einen Zeiger auf eine Funktion erhalte, möchte ich diese auch aufrufen. Da einen cast von void* auf Funktionspointer zu verhindern und somit auf die c++-style-casts zu verzichten, ist nicht so direkt möglich. Da muß ich dann entweder auf den c-style-cast oder auf den funktions-style-cast (@LordJaxom: ich glaube, wir verstehen uns jetzt 😉 ) zurück greifen.

    Tntnet



  • LordJaxom schrieb:

    std::string( "Hallo" ) // konstruiere einen std::string aus dem C-String "hallo"
    int( 13.4 ) // Wandle 13.4 in ein Integer um
    

    finde ich wesentlich schöner als

    (std::string) "Hallo" // oder gar
    static_cast<int>( 13.4 ) // irgh
    

    Das ist jetzt eine Frage, wie man das Bezeichnet. Ich lese bei int(13.4) einen Konstuktoraufruf. Und zwar gewissermassen den hypotetischen Konstruktor int::int(int), wobei das 13.4 implizit in ein int gecastet wird. Das ist also genau das selbe wie std::string("Hallo"). Das finde ich in C++ so schön, daß die internen Typen genau wie selbst definierte behandelt werden. Dein static_cast ist wirklich irgh oder auch 🙄. Ein wenig übertrieben, aber es ist schön, daß man es so explizit hin schreiben kann, wenn man diesen Umstand betonen möchte. Das ist halt Sprachreichtum.

    Tntnet



  • HumeSikkins schrieb:

    otze schrieb:

    camper schrieb:

    warum sollte man das wollen?

    [...]Natürlich spricht nichts gegen implizite casts[...]

    Bis auf die Tatsache, dass es soetwas nicht gibt 😉
    casts sind *explizite* Typkonvertierungen und können somit schwer implizit passieren.

    Ok, akzeptiert ;). Ist der ausdruck "implizite Typkonvertierung" besser?

    insbesondere dem function-style-cast entkommt man nur schwer, wenn man lesbaren und einigermaßen kompakten code schreiben will, schon ein simples

    std::string("foo")
    

    ist ja so etwas.

    Ich würde mal vermuten, dass dies mit einer impliziten Typkonvertierung in 99% der Fälle ohne cast hinhaut. Das letzte prozent geht für implizite Typkonvertierungen drauf, die nicht eindeutig sind.


Anmelden zum Antworten