Существуют ли двоичные потоки памяти в С++
Обычно я использую stringstream
для записи в строку в памяти. Есть ли способ записать в буфер char в двоичном режиме? Рассмотрим следующий код:
stringstream s;
s << 1 << 2 << 3;
const char* ch = s.str().c_str();
Память в ch
будет выглядеть так: 0x313233 - коды ASCII символов 1, 2 и 3. Я ищу способ написать сами двоичные значения. То есть, я хочу 0x010203 в памяти. Проблема в том, что я хочу написать функцию
void f(ostream& os)
{
os << 1 << 2 << 3;
}
И решите вне какой поток использовать. Что-то вроде этого:
mycharstream c;
c << 1 << 2 << 3; // c.data == 0x313233;
mybinstream b;
b << 1 << 2 << 3; // b.data == 0x010203;
Любые идеи?
Ответы
Ответ 1
Для чтения и записи двоичных данных в потоки, включая строковые потоки, используйте функции read() и write(). Так
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";
Это дает 0x4030201
в моей системе.
Изменить:
Чтобы сделать эту работу прозрачно с операторами вставки и извлечения (< < и → ), лучше всего создать для нее производный streambuf, который делает правильную вещь, и передать это тем потокам, которые вы хотите использовать.
Ответ 2
Ну, просто используйте символы, а не целые числа.
s << char(1) << char(2) << char(3);
Ответ 3
перегрузка некоторых необычных операторов работает довольно хорошо. Здесь ниже я решил перегрузить < =, потому что он имеет ту же самую ассоциацию слева направо, что и <, и имеет как-то близкий внешний вид...
#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;
}
Есть несколько недостатков:
-
новый смысл < = может запутать читателей или привести к неожиданным результатам:
cout <= 31 <= 32;
не даст тот же результат, что и
cout <= (31 <= 32);
-
endianess явно не упоминается при чтении кода, так как
как показано в приведенном выше примере.
-
он не может просто смешиваться с < <, потому что он не принадлежит к
той же группы приоритетов. Обычно я использую круглые скобки для
как:
( cout <= htonl(a) <= htonl(b) ) << endl;
Ответ 4
В этом случае я реализовал сам "оператор сдвига сдвига":
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;
}
Поместите его где-нибудь удобно и используйте его так:
std::cout <= 1337 <= 1337ULL <= 1337. <= 1337.f;
Преимущества:
- цепной
- автоматический
sizeof()
- также принимает массивы и экземпляры struct/class
Недостатки:
- небезопасно для объектов, отличных от POD: указатели утечек и отступы
- вывод специфичен для платформы: отступы, endianess, целые типы