Are there binary memory streams in C++

对着背影说爱祢 提交于 2019-11-27 11:18:25

To read and write binary data to streams, including stringstreams, use the read() and write() member functions. So

unsigned char a(1), b(2), c(3), d(4);
std::stringstream s;
s.write(reinterpret_cast<const char*>(&a), sizeof(unsigned char));
s.write(reinterpret_cast<const char*>(&b), sizeof(unsigned char));
s.write(reinterpret_cast<const char*>(&c), sizeof(unsigned char));
s.write(reinterpret_cast<const char*>(&d), sizeof(unsigned char));

s.read(reinterpret_cast<char*>(&v), sizeof(unsigned int)); 
std::cout << std::hex << v << "\n";

This gives 0x4030201 on my system.

Edit: To make this work transparently with the insertion and extraction operators (<< and >>), your best bet it to create a derived streambuf that does the right thing, and pass that to whatever streams you want to use.

Well, just use characters, not integers.

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

overloading some unusual operators works rather well. Here below I choosed to overload <= because it has the same left-to-right associativity as << and has somehow a close look-and-feel ...

#include <iostream>
#include <stdint.h>
#include <arpa/inet.h>

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;
}

There are several drawbacks :

  • the new meaning of <= may confuse readers or lead to unexpected results :

    cout <= 31 <= 32;
    

    won't give the same result as

    cout <= (31 <= 32);
    
  • the endianess isn't clearly mentionned at reading the code, as illustrated in the above example.

  • it cannot mix simply with << because it doesn't belong to the same group of precedence. I usually use parenthesis to clarify such as :

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

You can do this sort of thing with templates. E.g:

//struct to hold the value:
template<typename T> 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<typename T> bits_t<T&> bits(T &t) { return bits_t<T&>{t}; }
template<typename T> bits_t<const T&> bits(const T& t) { return bits_t<const T&>{t}; }
//insertion operator to call ::write() on whatever type of stream
template<typename S, typename T>
S& operator<<(S &s, bits_t<T> b) {
    return s.write((char*)&b.t, sizeof(T));
}
//extraction operator to call ::read(), require a non-const reference here
template<typename S, typename T>
S& operator>>(S& s, bits_t<T&> b) {
    return s.read((char*)&b.t, sizeof(T));
}

It could use some cleanup, but it's functional. E.g:

//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);

For this use case I implemented myself a "raw shift operator":

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

Put it somewhere convenient and use it like this:

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

Advantages:

  • chainable
  • automatic sizeof()
  • takes arrays and struct/class instances, too

Disadvantages:

  • unsafe for non-POD objects: leaks pointers and padding
  • output is platform specific: padding, endianess, integer types
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!