Более простой способ создать поток памяти C++ из (char*, size_t) без копирования данных?
Я не мог найти ничего готового, поэтому я придумал:
class membuf : public basic_streambuf<char>
{
public:
membuf(char* p, size_t n) {
setg(p, p, p + n);
setp(p, p + n);
}
}
Использование:
char *mybuffer;
size_t length;
// ... allocate "mybuffer", put data into it, set "length"
membuf mb(mybuffer, length);
istream reader(&mb);
// use "reader"
я знаю stringstream
, но он не может работать с двоичными данными заданной длины.
Я изобретаю здесь свое колесо?
РЕДАКТИРОВАТЬ
- Он не должен копировать входные данные, просто создать что-то, что будет перебирать данные.
- Он должен быть переносимым - по крайней мере, он должен работать как в gcc, так и в MSVC.
4 ответа
Я предполагаю, что ваши входные данные являются двоичными (не текстовыми), и вы хотите извлечь из них куски двоичных данных. Все без копирования ваших входных данных.
Вы можете объединить boost::iostreams::basic_array_source
а также boost::iostreams::stream_buffer
(от Boost.Iostreams) с boost::archive::binary_iarchive
(из Boost.Serialization), чтобы иметь возможность использовать удобные операторы извлечения >> для чтения фрагментов двоичных данных.
#include <stdint.h>
#include <iostream>
#include <boost/iostreams/device/array.hpp>
#include <boost/iostreams/stream.hpp>
#include <boost/archive/binary_iarchive.hpp>
int main()
{
uint16_t data[] = {1234, 5678};
char* dataPtr = (char*)&data;
typedef boost::iostreams::basic_array_source<char> Device;
boost::iostreams::stream_buffer<Device> buffer(dataPtr, sizeof(data));
boost::archive::binary_iarchive archive(buffer, boost::archive::no_header);
uint16_t word1, word2;
archive >> word1 >> word2;
std::cout << word1 << "," << word2 << std::endl;
return 0;
}
С GCC 4.4.1 на AMD64 он выводит:
1234,5678
Boost.Serialization очень мощная и знает, как сериализовать все основные типы, строки и даже контейнеры STL. Вы можете легко сделать ваши типы сериализуемыми. Смотрите документацию. Где-то в источниках Boost.Serialization скрыт пример переносимого двоичного архива, который знает, как выполнить правильную замену для порядка байтов вашего компьютера. Это может быть полезно для вас.
Если вам не нужна изящество Boost.Serialization и вы готовы читать двоичные данные в стиле fread(), вы можете использовать basic_array_source
проще:
#include <stdint.h>
#include <iostream>
#include <boost/iostreams/device/array.hpp>
#include <boost/iostreams/stream.hpp>
int main()
{
uint16_t data[] = {1234, 5678};
char* dataPtr = (char*)&data;
typedef boost::iostreams::basic_array_source<char> Device;
boost::iostreams::stream<Device> stream(dataPtr, sizeof(data));
uint16_t word1, word2;
stream.read((char*)&word1, sizeof(word1));
stream.read((char*)&word2, sizeof(word2));
std::cout << word1 << "," << word2 << std::endl;
return 0;
}
Я получаю такой же вывод с этой программой.
Я не уверен, что вам нужно, но делает ли это то, что вы хотите?
char *mybuffer;
size_t length;
// allocate, fill, set length, as before
std::string data(mybuffer, length);
std::istringstream mb(data);
//use mb
Стандартный буфер потока имеет эту функцию.
Создать поток. Получает свой буфер и перезаписывает его.
#include <sstream>
#include <iostream>
#include <algorithm>
#include <iterator>
int main()
{
// Your imaginary buffer
char buffer[] = "A large buffer we don't want to copy but use in a stream";
// An ordinary stream.
std::stringstream str;
// Get the streams buffer object. Reset the actual buffer being used.
str.rdbuf()->pubsetbuf(buffer,sizeof(buffer));
std::copy(std::istreambuf_iterator<char>(str),
std::istreambuf_iterator<char>(),
std::ostream_iterator<char>(std::cout)
);
}
Спрашивающий хотел что-то, что не копирует данные, и его решение отлично работает. Мой вклад состоит в том, чтобы немного его очистить, чтобы вы могли просто создать один объект, который является входным потоком для данных в памяти. Я проверил это, и это работает.
class MemoryInputStream: public std::istream
{
public:
MemoryInputStream(const uint8_t* aData,size_t aLength):
std::istream(&m_buffer),
m_buffer(aData,aLength)
{
rdbuf(&m_buffer); // reset the buffer after it has been properly constructed
}
private:
class MemoryBuffer: public std::basic_streambuf<char>
{
public:
MemoryBuffer(const uint8_t* aData,size_t aLength)
{
setg((char*)aData,(char*)aData,(char*)aData + aLength);
}
};
MemoryBuffer m_buffer;
};