signed/unsigned reinterpret_cast-en



  • Hallo

    Ich bräuchte mal wieder etwas Hilfe bei einer Designentscheidung, und zwar beim Umwandeln bzw. Benutzen von signed/unsigned Typen.
    Weil char ja nicht auf allen Systemen gleich ist (mal signed, mal unsigned) kam ich ein paar mal bei der Übergabe von char-arrays an Kompressions- oder Verschlüsselungsbibliotheken an die Stelle, wo die Bibliothek mit einem unsigned char * arbeiten wollte, bei mir aber Datenpuffer als char * verwendet werden.

    Da sehe ich dann zwei Möglichkeiten:

    1. reinterpret_cast
    2. konvertieren mit
      template<class DEST, class SRC> void convert(DEST *dest, SRC const *src, size_t len)
      {
      while(len-- > 0) (dest++) = DEST((src++));
      }

    Am typischen PC funktioniert reinterpret_cast zwischen signed/unsigned char, short, int, long, long long ... aber ist das auch auf anderen Platformen so?

    Oder sieht der C++ Standard hier etwas vor. Mir geht es ja etwas gegen den Strich, dass ein einfacher char meist signed ist... da würde ich einen Byte-Wert (also unsigned) erwarten und hab auch schon oft eine Bitverschiebung deshalb vermasselt.

    Darf man also sicher zwischen signed/unsigned Typen reinterpret_cast-en oder nicht?

    Bin für alle Infos dankbar 😉 weil nicht alle Optimierung dann auf anderen Systemen funktionieren.

    lG
    XOR 🙂


  • Mod

    Der Cast wird funktionieren, das ist garantiert. Aber es ist nicht garantiert, dass auf jeder Plattform genau das gleiche Ergebnis rauskommt.

    Aber zwei Fragen:
    Warum arbeitest du dann nicht immer mit unsigned?

    Warum reinterpet_cast und nicht static_cast? Da hättest du ein sauber definiertes Ergebnis wenn du eine negative Zahl in eine unsigned-Zahl umwandelst (Zurück aber leider nicht 😞 ). Dazu müsstest du natürlich die gesamte Zeichenkette abhangeln, den Pointer kannst du nicht so einfach umcasten.



  • Ich habe kürzlich in einem Kommentar gelesen, dass der C Standard nicht einmal garantiert, dass signed/unsigned Umwandlungen durch ein 2er Komplement möglich sind... Sieht also etwas böse aus für reinterpret_cast.

    Die Entscheidung für char * fiel wegen Strings und anderen APIs (sockets ...). Das Problem wäre aber dort genau so vorhanden.

    Mir geht es eigentlich weniger um den Wert der Zahlen als um deren Transport. Schönes beispiel: Ich habe ein größeres XML-packet (char*). Ich komprimiere es mit zlib (unsigned char ), verschlüssele es crypto++ (char).

    Vielleicht könnte man es so formulieren:
    Auf Systemen, auf denen für
    char x = N;
    ++((unsigned char)&x);
    --x;
    zum Ergebnis (N == x) führt, kann man reinterpret_cast einsetzen.

    Ich möchte es natürlich vermeiden immer einen Puffer zu konvertieren. Falls mir dann aber ein zlib-komprimiertes datenpacket von einem mobilen gerät nicht mehr auf dem PC dekomprimiert werden kann, schreibe ich lieber eine für alle Systeme einheitliche Konvertierung ...

    Zum static_cast: Da bin ich jetzt etwas verwirrt. Ich dachte eben dass ein doppelter static_cast immer zum Ausgang zurückfindet:
    char x = -42;
    static_cast<char>(static_cast<unsigned char>(x)) == x
    Und genau dass wollte ich mit dem template bewirken, nur eben mit der Konstruktor-Syntax (sollte ja bei integralen Typen einem static_cast gleichkommen, oder?)

    Ich frage mich, wie andere embedded-systems damit umgehen...

    PS: Ein anderes Beispiel sind UTF Konvertierungen. Wenn ich die Bits eines chars herausziehe um die Länge des UTF chars zu lesen ... darf man denn dann überhaupt bit-Operationen auf negative typen anwenden, wenn der C-Standard kein 2er-Komplement Zahlensystem erzwingt??

    lg
    XOR


  • Mod

    xor schrieb:

    Zum static_cast: Da bin ich jetzt etwas verwirrt. Ich dachte eben dass ein doppelter static_cast immer zum Ausgang zurückfindet:

    Das dürfte wohl so sein, ist aber nicht standardisiert. Die Umwandlung von unsigned in signed ist implementation defined, sobald der Wert größer ist als die größte positive Zahl des signed Typen. Insbesondere heißt dies, dass du nicht unbedingt wieder den gleichen Wert bekommen könntest, wenn du die Hin und Rückwandlung auf verschiedenen Systemen durchführst. Ich sollte aber noch sagen, dass dies die absolute Ausnahmesituationen ist. Vielleicht beim Zusammenspiel von irgendwelchen exotischen embedded-systems.

    PS: Ein anderes Beispiel sind UTF Konvertierungen. Wenn ich die Bits eines chars herausziehe um die Länge des UTF chars zu lesen ... darf man denn dann überhaupt bit-Operationen auf negative typen anwenden, wenn der C-Standard kein 2er-Komplement Zahlensystem erzwingt??

    Richtig erkannt 🙂 . Ist auch implementation defined, wenn die Zahl negativ ist.



  • Zum static_cast: Da bin ich jetzt etwas verwirrt. Ich dachte eben dass ein doppelter static_cast immer zum Ausgang zurückfindet:

    Technisch ist das Richtig, da sich die Bitmuster ja nicht ändern. Mathematisch stimmt das nicht, da der Wertebereich bei unsigned ein anderer ist der nicht komplett auf signed abgebildet werden kann.

    Das spielt dann z.B. bei Filteroperationen eine Rolle.



  • Oh mein Gott 😞 ... das ist ja schlimmer als ich dachte ... 😃
    Danke für eure Infos.
    Ich will es damit also nicht zu weit treiben und beschränke mich eben darauf, dass mein Code nur auf Systemen funktioniert, auf denen signed->unsigned->signed zum Ursprung zurückfindet (was ja auf fast allen so sein wird).
    Ich konnte bisher auch keine Bibliothek finden, die bei der Umwandlung von (char*) in (unsigned char*) etwas anderes macht als reinterpret_cast (bzw einen C-cast), also mache ich es eben auch so.

    Aber etwas bedenklich finde ich die Situation schon. Gerade bei char wird viel herumkonvertiert weil bei binären Daten das Byte (unsigned) und bei Texten der Character (signed) notwendig sind und beide Gruppen jeweils miteinander verbunden werden müssen (für Dateien, Netzwerkprotokolle, ...).

    Falls jemand eine CPU kennt, die bei solchen Konvertierungen tatsächlich Daten verliert oder undefiniert zurückgibt, würde mich deren Name interessieren 😉

    lg XOR 🙂



  • Was willst du eigentlich programmieren und auf welchen Systemen soll das laufen?



  • Meine Frage war eher allgemeiner Natur. Aber konkret geht es um Netzwerkprotokolle bzw. binäre Serialisierung. Bereits beim "normalen" PC-Client kamen mir da ein paar Fehler unter zwischen 32-bit und 64-bit wo einfach eine struct mit diversen integer in char* - Array reinterpret_cast-et wurde um sie dann zu versenden. Hier fehlte etwa eine system-unabhängige Serialisierung. Seitdem sehe ich mir andere Code-Blöcke etwas genauer an und bin auf der Suche nach einem vorausschauendem Programmierstil für alle diese Fälle. Mit signed/unsigned hatte ich selbst weder unter Windows/Linux noch unter iPhone/WinCE Probleme ... aber sich am C++-Standard zu orientieren was definierte/undefinierte Zustände anbelangt ist sicher kein Fehler 😉

    Und so lerne ich eben, dass mur weil am PC eben was geht, es noch lange nicht korret ist.

    Von mir sind jetzt zwar keine anderen Platformen geplant ... aber es wäre natürlich nett wenn man sagen könnte: "Der Protokoll-Layer läuft überall".



  • Wenn man binäre Daten serialisiert, ist es natürlich dumm, dass mit reinterpret cast zu machen, vorallem, wenn man zwischen 64 und 32 bit kommuniziert. Da macht man sich dann eine read/write Funktion für die Datentypen.



  • waassz schrieb:

    Wenn man binäre Daten serialisiert, ist es natürlich dumm, dass mit reinterpret cast zu machen, vorallem, wenn man zwischen 64 und 32 bit kommuniziert. Da macht man sich dann eine read/write Funktion für die Datentypen.

    Hab ich ja ... mit templates sogar 😃
    ... und dann stellt sich eben die Frage ob das Ziel char* oder unsigned char* ist. Und wenn man die Daten dann komprimieren/verschlüsseln und versenden will, braucht man eben beide Formen -> signed UND unsigned -> siehe oben 😉


Anmelden zum Antworten