Более простой способ создания потока памяти C ++ из (char *, size_t) без копирования данных? - PullRequest
29 голосов
/ 17 января 2010

Я не смог найти ничего готового, поэтому придумал:

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, но, похоже, он не может работать с двоичными данными заданной длины.

Я изобретаю здесь свое колесо?

EDIT

  • Это не должно копировать входные данные, просто создать что-то, что будет перебирать данные.
  • Он должен быть переносимым - по крайней мере, он должен работать как в gcc, так и в MSVC.

Ответы [ 4 ]

28 голосов
/ 17 января 2010

Я предполагаю, что ваши входные данные являются двоичными (не текстовыми), и вы хотите извлечь из них куски двоичных данных. Все без копирования ваших входных данных.

Вы можете объединить 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;
}

Я получаю такой же вывод с этой программой.

5 голосов
/ 17 января 2010

Я не уверен, что вам нужно, но делает ли это то, что вы хотите?

char *mybuffer;
size_t length;
// allocate, fill, set length, as before

std::string data(mybuffer, length);
std::istringstream mb(data);
//use mb
4 голосов
/ 17 января 2010

Стандартный потоковый буфер имеет эту функцию.
Создать поток. Получает свой буфер и перезаписывает его.

#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)
             );
}
2 голосов
/ 16 мая 2012

Спрашивающий хотел что-то, что не копирует данные, и его решение отлично работает. Мой вклад состоит в том, чтобы немного его очистить, чтобы вы могли просто создать один объект, который является входным потоком для данных в памяти. Я проверил это, и это работает.

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