Portabilität size_t



  • Hey,

    wie portabel ist size_t? Ich muss ein 4 Byte großes size_t aus einer Datei (binär) laden. Welche Probleme könnten auftreten? Wie kann ich diese lösen?

    Noch eine weitere Frage: Wie portabel ist eine AES implementation? Kann es dort Probleme geben oder sind solche Implementation meist portabel?

    Vielen Dank
    Dirkyy


  • Mod

    size_t ist definiert als der vorzeichenlose Ganzzahltyp, der von sizeof zurückgegeben wird. Was man da reininterpretiert bleibt dem Implementierer überlassen.


  • Administrator

    Dirkyy schrieb:

    wie portabel ist size_t? Ich muss ein 4 Byte großes size_t aus einer Datei (binär) laden. Welche Probleme könnten auftreten? Wie kann ich diese lösen?

    Wie SeppJ gesagt hat. Allerdings gibt es zum Teil Möglichkeiten, entsprechend grosse Typen zu bekommen. Zum Beispiel hat man bei Boost:
    http://www.boost.org/doc/libs/1_41_0/libs/integer/cstdint.htm

    Man kann allerdings auch eine einfache Möglichkeit bauen, um zu überprüfen, ob std::size_t mindestens 4 Bytes hat:

    template<bool check>
    struct StaticAssert;
    
    template<>
    struct StaticAssert<true> { };
    
    template<typename T, size_t bytes>
    struct HasAtLeastBytes
    {
      StaticAssert<sizeof(T) >= bytes> check;
    };
    
    #include <cstddef> // Für std::size_t
    
    int main()
    {
      HasAtLeastBytes<std::size_t, 4> checkSizeT;
    
      return 0;
    }
    

    Falls std::size_t weniger als 4 Bytes beim Kompilieren hat, wird die Kompilation abbrechen. Du kannst es zum Beispiel mal so testen:

    int main()
    {
      HasAtLeastBytes<std::size_t, 40> checkSizeT;
    
      return 0;
    }
    

    Das sollte seeeehr wahrscheinlich nicht kompilieren 🙂

    Dirkyy schrieb:

    Noch eine weitere Frage: Wie portabel ist eine AES implementation? Kann es dort Probleme geben oder sind solche Implementation meist portabel?

    Kommt wohl ganz auf die Implementation drauf an. Wobei AES natürlich ein Standard ist, steht ja im Namen 🙂



  • Ich muss ein 4 Byte großes size_t aus einer Datei (binär) laden.

    Dann nimm doch einen Datentyp der garantiert diese Groesse hat, z.B. uint32_t aus stdint.h. Ab C++0X soll dann auch #include <cstdint> funktionieren, g++ moniert aber, ich sollte doch bitte den entsprechenden Kompilerschalter fuer den zukuenftigen Standard setzen.


  • Administrator

    knivil schrieb:

    Ich muss ein 4 Byte großes size_t aus einer Datei (binär) laden.

    Dann nimm doch einen Datentyp der garantiert diese Groesse hat, z.B. uint32_t aus stdint.h

    Nur ist stdint.h kein Standard C++ Header 😉
    Und cstdint gibt es auch nicht.

    Grüssli



  • Nur ist stdint.h kein Standard C++ Header

    Natuerlich nicht, weil die Groesse von den verschiedenen Integertypen im Standard nicht definiert sind. Es gibt deswegen keinen Weg, mit Standardmitteln zu garantieren, dass ein Integer eine betimmte Groesse hat. Das sagt mir mein g++ bei #include <cstdint>:

    In Datei, eingefügt von /usr/lib/gcc/i586-redhat-linux/4.4.1/../../../../include/c++/4.4.1/cstdint:35,
    von main.cpp:7:
    /usr/lib/gcc/i586-redhat-linux/4.4.1/../../../../include/c++/4.4.1/c++0x_warning.h:31:2: Fehler: #error This file requires compiler and library support for the upcoming ISO C++ standard, C++0x. This support is currently experimental, and must be enabled with the -std=c++0x or -std=gnu++0x compiler options.


  • Administrator

    knivil schrieb:

    Nur ist stdint.h kein Standard C++ Header

    Natuerlich nicht, weil die Groesse von den verschiedenen Integertypen im Standard nicht definiert sind. Es gibt deswegen keinen Weg, mit Standardmitteln zu garantieren, dass ein Integer eine betimmte Groesse hat.

    Was redest du da?
    stdint.h ist ein C99 Header und hat eben keinen Bezug zum aktuellen C++ Standard. Und wenn der Autor schon wegen Portabilität fragt, dürfte dies wirklich der falsche Ansatz sein.

    Und doch es gibt einen Weg, den habe ich gezeigt.

    Grüssli



  • Und doch es gibt einen Weg, den habe ich gezeigt.
    ...
    Falls std::size_t weniger als 4 Bytes beim Kompilieren hat, wird die Kompilation abbrechen.

    Tja und dann?

    stdint.h ist ein C99 Header und hat eben keinen Bezug zum aktuellen C++ Standard. Und wenn der Autor schon wegen Portabilität fragt, dürfte dies wirklich der falsche Ansatz sein.

    Warum ist das der falsche Weg, wenn der eine Standard (hier C++) etwas nicht hergibt, einen anderen (hier C99) zu verwenden?


  • Administrator

    knivil schrieb:

    Und doch es gibt einen Weg, den habe ich gezeigt.
    ...
    Falls std::size_t weniger als 4 Bytes beim Kompilieren hat, wird die Kompilation abbrechen.

    Tja und dann?

    Dann hast du die Sicherheit, dass du nicht ein Programm kompiliert hast, welches undefiniertes Verhalten auslöst. Es ist eine Sicherung mehr nicht. Da aber die verbreitesten und meisten Plattformen wohl std::size_t mit 4 oder mehr Bytes versehen werden, wird der Code meistens kompilieren.

    Ansonsten kann man eben auch Boost verwenden. Die vereinen alle nicht portablen Erweiterungen und bieten einen portablen Header an.

    knivil schrieb:

    Warum ist das der falsche Weg, wenn der eine Standard (hier C++) etwas nicht hergibt, einen anderen (hier C99) zu verwenden?

    Schon mal gehört, was ein C++ Kompiler ist? Der unterstützt den C++ Standard und keinen anderen Standard. Wenn er irgendetwas in der Richtung tut, dann sind das nicht portable Erweiterungen.

    Grüssli



  • Dirkyy schrieb:

    Welche Probleme könnten auftreten? Wie kann ich diese lösen?

    Schau mal bei Wiki nach Endianess, sollte man auch bedenken.



  • Der unterstützt den C++ Standard und keinen anderen Standard. Wenn er irgendetwas in der Richtung tut, dann sind das nicht portable Erweiterungen.

    Gut, dann gibt es nur eine Loesung: Es gibt keine Loesung in C++. Ansonsten: Diese Sachen einfach in einer C-Bibliothek kapseln, dafuer einen C-Compiler nutzen und dazulinken. Aber ich weiss grad nicht, ob der C++ Standard irgendwas uebers Linken aussagt ... Ich finde deine Argumentation zwar richtig, aber auch etwas engstirnig. Der Weg ueber stdint.h ist fuer mich der beste, wenn man exakte Groessen (nein, nicht 4 Byte oder mehr, sondern exakt 4 Byte) haben moechte. Was tun, wenn es auf 64 Bit Machinen keine 4 Byte gross ist und ich auf Bitoperationen vertraue, die nur fuer 4 Byte ausgelegt sind? ...



  • #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    int main() {
      char buf[4];
      std::cin.read(buf, sizeof(buf));
    #ifdef CONVERT_ENDIAN
      std::reverse(buf, buf+sizeof(buf));
    #endif
      std::size_t s;
      std::memcpy(reinterpret_cast<char*>(&s)
    #ifndef LITTLE_ENDIAN
                  + sizeof(s)-sizeof(buf)
    #endif
                  , buf, sizeof(buf));
      std::cout << s << std::endl;
    }
    

    cstdint gibt es übrigens seit TR1 🙄

    #include <algorithm>
    #include <iostream>
    #ifndef CPP0X
    #include <tr1/cstdint>
    using std::tr1::uint32_t;
    #else
    #include <cstdint>
    using std::uint32_t;
    #endif
    
    int main() {
      uint32_t i;
      std::cin.read(reinterpret_cast<char*>(&i), sizeof(i));
    #ifdef CONVERT_ENDIAN
      std::reverse(reinterpret_cast<char*>(&i),
                   reinterpret_cast<char*>(&i) + sizeof(i));
    #endif
      std::cout << i << std::endl;
    }
    

  • Administrator

    knivil schrieb:

    Gut, dann gibt es nur eine Loesung: Es gibt keine Loesung in C++.

    Leider keine befriedigende, ja.

    knivil schrieb:

    Ansonsten: Diese Sachen einfach in einer C-Bibliothek kapseln, dafuer einen C-Compiler nutzen und dazulinken.

    Geht auch nicht. Wie willst du dann auf den Typ zugreifen?

    knivil schrieb:

    Ich finde deine Argumentation zwar richtig, aber auch etwas engstirnig.

    Naja, ich hebe nur den Finger. Du erzählst Dinge und setzt es so hin, als wenn sie portabel und überall korrekt wären, was schlicht und einfach nicht stimmt. Wenn du deine Lösungen mit Warnzeichen oder dem richtigen Kontext eingeschlossen hättest und auf die Probleme hingewiesen hättest, dann hätte ich nichts dagegen gesagt.

    Es wurde nach einer portablen Lösung gefragt, was deine Lösungen allesamt nicht waren.

    knivil schrieb:

    Der Weg ueber stdint.h ist fuer mich der beste, wenn man exakte Groessen (nein, nicht 4 Byte oder mehr, sondern exakt 4 Byte) haben moechte. Was tun, wenn es auf 64 Bit Machinen keine 4 Byte gross ist und ich auf Bitoperationen vertraue, die nur fuer 4 Byte ausgelegt sind? ...

    1. Du kannst mein präsentierter Code so umbauen, dass er die Überprüfung macht, ob es genau 4 Bytes sind 😉
    2. Man kann entsprechende Bit-Operationen durch AND-Bit-Operationen absichern. Mache ich fast immer, damit korrekt validiert wird.

    rüdiger schrieb:

    cstdint gibt es übrigens seit TR1

    Wusste ich nicht, danke für die Info! Wobei wenn ich weiterhin mit dem Finger weitermachen möchte: TR1 ist kein Standard, sondern nur ein Technical Report und muss nicht vorhanden sein, um dem C++ Standard 2003 zu genügen. 😃

    rüdiger schrieb:

    ... /* convert endianess */ ...
    

    Und was ist bei Middle-Endian? *SCNR* 🤡

    Grüssli



  • Dravere schrieb:

    Wusste ich nicht, danke für die Info! Wobei wenn ich weiterhin mit dem Finger weitermachen möchte: TR1 ist kein Standard, sondern nur ein Technical Report und muss nicht vorhanden sein, um dem C++ Standard 2003 zu genügen. 😃

    Der Op hat aber auch nichts von C++03 gesagt. Vielleicht sucht er ja eine portable Lösung für all seine C++0x Compiler :p. Vermutlich ist der eh geflohen und denkt wir sind alle verrückt :).

    Dravere schrieb:

    rüdiger schrieb:

    ... /* convert endianess */ ...
    

    Und was ist bei Middle-Endian? *SCNR* 🤡

    Hmm, ich hätte eher gedacht, dass man sich darüber beschwert, wenn char keine 8 Bit hat :p



  • Danke für eure Antworten 🙂

    Es ist für mich natürlich nicht direkt notwendig, dass ich exakt den Typ size_t nutze. Ich kann also auch unsigned int schreiben oder einen anderen Typen, der diese Bedingungen erfüllt. Würde das einen Unterschied machen?

    Der Grund für die 4 Byte-Limitierung ist ein Dateiformat, dass ich unterstützen will und dies leider voraussetzt. Auch Bit-Operationen werden später ausgeführt.

    Die von Rüdiger vorgeschlagene Absicherung ist zwar ganz nett, aber wenn ich das Programm (in kompilierter) Form weitergebe, dann bringt sie nichts mehr.


  • Administrator

    Dirkyy schrieb:

    Es ist für mich natürlich nicht direkt notwendig, dass ich exakt den Typ size_t nutze. Ich kann also auch unsigned int schreiben oder einen anderen Typen, der diese Bedingungen erfüllt. Würde das einen Unterschied machen?

    unsigned int würde nicht viel bringen, da auch unsigned int nicht genau definiert ist. Am besten setzt du auf <tr1/cstdint> oder auf den Boost Header. Auch wenn sie nicht ganz portabel sind.

    Dirkyy schrieb:

    Die von Rüdiger vorgeschlagene Absicherung ist zwar ganz nett, aber wenn ich das Programm (in kompilierter) Form weitergebe, dann bringt sie nichts mehr.

    1. Rüdiger hat nur eine zusätzliche Methode präsentiert, wie man es einlesen könnte. Zudem hat er gezeigt, wie man mit Little- und Big-Endian umgehen kannt. Eine Absicherung hast du dort nicht.
    2. In kompilierter Form ändern sich die Grössen nicht mehr. Wenn der Kompiler gesagt hat, dass ein std::size_t 4 Bytes ist, dann wird das so in Assembler/Maschinencode hingeschrieben und wird auch so bleiben.

    Grüssli



  • Dirkyy schrieb:

    Die von Rüdiger vorgeschlagene Absicherung ist zwar ganz nett, aber wenn ich das Programm (in kompilierter) Form weitergebe, dann bringt sie nichts mehr.

    Warum das? Das zweite Beispiel benutzt doch einen Typ fester Größe. Beide Beispiele funktionieren unabhängig von der eigentlichen Größe von size_t.

    Falls du die Endianess meinst: Wenn du auf einem System mit anderer Endianess arbeiten musst, dann musst du eh neu kompilieren.

    Also beide Lösungen sollten funktionieren.



  • rüdiger schrieb:

    Dirkyy schrieb:

    Die von Rüdiger vorgeschlagene Absicherung ist zwar ganz nett, aber wenn ich das Programm (in kompilierter) Form weitergebe, dann bringt sie nichts mehr.

    Warum das? Das zweite Beispiel benutzt doch einen Typ fester Größe. Beide Beispiele funktionieren unabhängig von der eigentlichen Größe von size_t.

    Falls du die Endianess meinst: Wenn du auf einem System mit anderer Endianess arbeiten musst, dann musst du eh neu kompilieren.

    Also beide Lösungen sollten funktionieren.

    Mein Fehler, tut mir leid 🙂

    Könntest du mir noch etwas zur Funktionsweise deines Codes sagen? Was hat es mit der Funktion std::memcpy auf sich? Was wird dort wohin kopiert? Der Rest des Codes ist mir klar 🙂

    Und kann ich die Idee dahinter auf andere (nicht portable) Typen auch anwenden?



  • Dirkyy schrieb:

    Hey,

    wie portabel ist size_t? Ich muss ein 4 Byte großes size_t aus einer Datei (binär) laden.

    Binärdateien sind nicht portabel. Wenn es unbedingt ein Binärformat sein muß, dann muß man einen Header davorschreiben, in dem beschrieben ist, wie das Format aufgebaut ist. Bei den verbreiteten Plattformen hast Du vier verschiedene Formate für size_t: 32Bit und 64Bit sowie Big und Little Endian.


Anmelden zum Antworten