Existe-t-il des stream de mémoire binary en C ++

J’utilise habituellement ssortingngstream pour écrire dans une chaîne en mémoire. Est-il possible d’écrire dans un tampon de caractères en mode binary? Considérez le code suivant:

 ssortingngstream s; s << 1 << 2 << 3; const char* ch = s.str().c_str(); 

La mémoire à ch ressemblera à ceci: 0x313233 – les codes ASCII des caractères 1, 2 et 3. Je cherche un moyen d’écrire les valeurs binarys elles-mêmes. C’est-à-dire que je veux 0x010203 dans la mémoire. Le problème est que je veux pouvoir écrire une fonction

 void f(ostream& os) { os << 1 << 2 << 3; } 

Et décider en dehors de quel type de stream à utiliser. Quelque chose comme ça:

 mycharstream c; c << 1 << 2 << 3; // c.data == 0x313233; mybinstream b; b << 1 << 2 << 3; // b.data == 0x010203; 

Des idées?

Pour lire et écrire des données binarys sur des stream, y compris des chaînes de caractères, utilisez les fonctions membres read () et write (). Alors

 unsigned char a(1), b(2), c(3), d(4); std::ssortingngstream s; s.write(reinterpret_cast(&a), sizeof(unsigned char)); s.write(reinterpret_cast(&b), sizeof(unsigned char)); s.write(reinterpret_cast(&c), sizeof(unsigned char)); s.write(reinterpret_cast(&d), sizeof(unsigned char)); s.read(reinterpret_cast(&v), sizeof(unsigned int)); std::cout << std::hex << v << "\n"; 

Cela donne 0x4030201 sur mon système.

Edit: Pour que cela fonctionne de manière transparente avec les opérateurs d'insertion et d'extraction (<< et >>), il est préférable de créer un streambuf dérivé correct et de le transmettre aux stream que vous souhaitez utiliser.

Eh bien, utilisez simplement des caractères, pas des entiers.

 s << char(1) << char(2) << char(3); 

la surcharge de certains opérateurs inhabituels fonctionne plutôt bien. Ci-dessous, j’ai choisi de surcharger <= parce qu’il a la même associativité de gauche à droite que << et qu’il a en quelque sorte une apparence proche …

 #include  #include  #include  using namespace std; ostream & operator<= (ostream& cout, string const& s) { return cout.write (s.c_str(), s.size()); } ostream & operator<= (ostream& cout, const char *s) { return cout << s; } ostream & operator<= (ostream&, int16_t const& i) { return cout.write ((const char *)&i, 2); } ostream & operator<= (ostream&, int32_t const& i) { return cout.write ((const char *)&i, 4); } ostream & operator<= (ostream&, uint16_t const& i) { return cout.write ((const char *)&i, 2); } ostream & operator<= (ostream&, uint32_t const& i) { return cout.write ((const char *)&i, 4); } int main() { string s("some binary data follow : "); cout <= s <= " (machine ordered) : " <= (uint32_t)0x31323334 <= "\n" <= s <= " (network ordered) : " <= htonl(0x31323334) ; cout << endl; return 0; } 

Il y a plusieurs inconvénients:

  • la nouvelle signification de <= peut confondre les lecteurs ou conduire à des résultats inattendus:

     cout <= 31 <= 32; 

    ne donnera pas le même résultat que

     cout <= (31 <= 32); 
  • L'endianess n'est pas clairement mentionné à la lecture du code, comme illustré dans l'exemple ci-dessus.

  • il ne peut pas se mélanger simplement avec << car il n'appartient pas au même groupe de priorité. J'utilise habituellement des parenthèses pour clarifier, par exemple:

     ( cout <= htonl(a) <= htonl(b) ) << endl; 

Pour ce cas d’utilisation, j’ai implémenté moi-même un “opérateur de poste brut”:

 template  inline std::basic_ostream & operator <= (std::basic_ostream & out, T const & data) { out.write(reinterpret_cast(&data), sizeof(T)); return out; } 

Mettez-le dans un endroit pratique et utilisez-le comme ceci:

 std::cout <= 1337 <= 1337ULL <= 1337. <= 1337.f; 

Avantages:

  • chaînable
  • sizeof() automatique sizeof()
  • prend aussi des tableaux et des instances de struct / class

Désavantages:

  • dangereux pour les objects non-POD: pointeurs de fuite et remplissage
  • la sortie est spécifique à la plate-forme: padding, endianess, types entiers

Vous pouvez faire ce genre de chose avec des modèles. Par exemple:

 //struct to hold the value: template struct bits_t { T t; }; //no constructor necessary //functions to infer type, construct bits_t with a member initialization list //use a reference to avoid copying. The non-const version lets us extract too template bits_t bits(T &t) { return bits_t{t}; } template bits_t bits(const T& t) { return bits_t{t}; } //insertion operator to call ::write() on whatever type of stream template S& operator<<(S &s, bits_t b) { return s.write((char*)&b.t, sizeof(T)); } //extraction operator to call ::read(), require a non-const reference here template S& operator>>(S& s, bits_t b) { return s.read((char*)&b.t, sizeof(T)); } 

Il pourrait utiliser un peu de nettoyage, mais c’est fonctionnel. Par exemple:

 //writing std::ofstream f = /*open a file*/; int a = 5, b = -1, c = 123456; f << bits(a) << bits(b) << bits(c); //reading std::ifstream f2 = /*open a file*/; int a, b, c; f >> bits(a) >> bits(b) >> bits(c);