【问题标题】:Provide a std::istream interface to an existing buffer without copying it为现有缓冲区提供 std::istream 接口而不复制它
【发布时间】:2011-07-19 06:28:41
【问题描述】:

我有一个固定长度的字符缓冲区,我想将它提供给一个接受 std::istream& 的函数。在不复制缓冲区的情况下如何做到这一点?

如果这意味着派生自定义流缓冲区,我想我会接受副本。我只是想知道我是否遗漏了一些简单的东西。

这是我现在正在做的事情(不需要的副本):

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

编辑:

为了记录,这是使用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 解决方案。谢谢大家。

【问题讨论】:

  • 您是否检查过在这种情况下副本没有被优化掉?
  • 仅供参考,std::istrstream 提供此功能。它有构造函数(char*, streamsize)。但是,您可能知道,此类已被弃用...

标签: c++ iostream


【解决方案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;

【讨论】:

  • 感谢您的建议。至于代码,库不是已经提供了这个功能吗?我正在查看boost::iostreams::array_source 并快速浏览一下文档,它看起来好像在做同样的事情。也许我错过了什么。
  • @Martin Stone:我的解决方案给你的唯一一件事是array_source 没有,那就是我的解决方案是可寻求的。如果您不需要,请使用array_source
  • @dalle 有没有办法返回指向内部缓冲区_的指针? (对我来说是特殊情况)如何找出缓冲区中可用的有效(写入)数据?
  • @dalle 当我使用它(并且还提升 ibufferstream)时有一个奇怪的行为,因为当 ConstBufferDevice 返回 -1(EOF) 时我没有得到 EOF
【解决方案2】:

一种选择是提供std::streambuf 的自定义实现,它引用外部(而不是内部)缓冲区。一旦你有了这个,你就可以继承std::istream 并提供一个构造函数,将istream 的流缓冲区设置为指向你的缓冲区的自定义streambuf 类型的实例。这立即为您提供了由外部缓冲区支持的istream 的全部功能。 IIRC,实现streambuf 的大部分复杂性都涉及在数据用完时重新填充缓冲区的复杂性,但在你的情况下,这应该是微不足道的,因为你可以报告你的数据用完了。

希望这会有所帮助!

【讨论】:

  • OP 说他不想继承 std::streambuf 的子类。
  • @dark_charlie:我很确定如果没有子类化 streambuf 就无法做到这一点。这个答案的要点是,我认为这并不像最初看起来那么困难。
  • 好的。不过,复制要容易得多,如果缓冲区不大,性能问题可以忽略不计。
  • 我认为没有子类化是可能的。请参阅我的解决方案。有错请告诉我!
  • 请注意,Boost.Iostreams 的使用使实现您自己的流缓冲区更加更简单。
【解决方案3】:

我怀疑这是可能的。 STL 容器/流等总是想要拥有缓冲区,因此他们复制它。我知道没有简单的方法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-10-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多