Предоставить интерфейс std:: istream для существующего буфера без его копирования

У меня есть буфер символов фиксированной длины, и я хотел бы передать его функции, которая принимает std:: istream &. Как я могу это сделать без копирования буфера?

Если это означает получение пользовательского streambuf, я думаю, что я буду жить с копией. Мне просто интересно, не хватает ли я чего-то простого.

Вот что я делаю сейчас (что делает нежелательная копия):

void loadFromBuffer(const char* buff, size_t len) {
    std::istringstream is(std::string(buff, len)); // BUFFER COPIED HERE :(
    load(is);
}

Edit:

Для записи здесь простое решение с использованием boost.Iostreams:

#include <boost/iostreams/device/array.hpp>
#include <boost/iostreams/stream.hpp>

void loadFromBuffer2(char* buff, size_t len) {
    typedef boost::iostreams::stream<boost::iostreams::array_source> array_stream;
    array_stream is(buff, len);
    load(is);
}

Я принял ответ Boost.Iostreams, потому что он кажется "правильным" решением, однако он не компилируется на моей платформе (Android NDK), поэтому я закончил использование устаревшего решения std:: istrstream. Спасибо всем.

Ответ 1

Я сам был в подобной ситуации, и вместо того, чтобы создавать все, я использовал Boost.Iostreams, создав исходное устройство только для чтения.

Неподтвержденный, но это может работать:

class ConstBufferDevice
{
public:
   typedef char char_type;

   struct category :
      virtual boost::iostreams::device_tag,
      virtual boost::iostreams::input_seekable
   {
   };

   ConstBufferDevice(const char_type* buffer, size_t buffersize)
      : buffer_(buffer)
      , buffersize_(buffersize)
      , pos_(0)
   {
   }

   std::streamsize read(char_type* buffer, std::streamsize buffersize)
   {
      const std::streamsize amount = static_cast<std::streamsize>(buffersize_ - pos_);
      const std::streamsize result = (std::min)(buffersize, amount);
      if (result != 0)
      {
         std::copy(buffer_ + pos_, buffer_ + pos_ + result, buffer);
         pos_ += result;
         return result;
      }
      else
      {
         return buffersize ? -1 : 0; // EOF
      }
   }

   std::streampos seek(boost::iostreams::stream_offset offset,
                       std::ios_base::seekdir seekdir)
   {
      // Determine new value of pos_
      boost::iostreams::stream_offset newpos;

      if (seekdir == std::ios_base::beg)
      {
         newpos = offset;
      }
      else if (seekdir == std::ios_base::cur)
      {
         newpos = pos_ + offset;
      }
      else if (seekdir == std::ios_base::end)
      {
         newpos = buffersize_ + offset;
      }
      else
      {
         throw std::ios_base::failure("bad seek direction");
      }

      // Check for errors
      if (newpos < 0 || newpos > buffersize_)
      {
         throw std::ios_base::failure("bad seek offset");
      }

      pos_ = static_cast<size_t>(newpos);
      return boost::iostreams::offset_to_position(newpos);
   }

private:
   const char_type* buffer_;
   size_t buffersize_;
   size_t pos_;
};

typedef boost::iostreams::stream<ConstBufferDevice> ConstBufferStream;

Ответ 2

Один из вариантов - предоставить настраиваемую реализацию std::streambuf, которая ссылается на внешний (а не внутренний) буфер. После этого вы можете подклассифицировать std::istream и предоставить конструктор, который устанавливает буфер потока istream, чтобы быть экземпляром вашего настраиваемого типа streambuf, который указывает на ваш буфер. Это сразу дает вам полные возможности istream, которые будут поддерживаться вашим внешним буфером. IIRC, большая часть сложности реализации streambuf связана с сложностями заполнения буфера при завершении данных, но в вашем случае это должно быть тривиально, поскольку вы можете просто сообщить, что у вас нет данных.

Надеюсь, это поможет!

Ответ 3

Я сомневаюсь, что это возможно. Контейнеры/потоки STL и т.д. Всегда хотят владеть буфером, поэтому они копируют его. Нет простого способа, о котором я знаю.