简体   繁体   English

boost::iostreams 管理资源

[英]boost::iostreams managing resources

I'm new to boost and its iostreams package and finding the documentation a bit thin.我是 boost 和它的 iostreams package 的新手,发现文档有点薄。 Hopefully someone will set me straight.希望有人能纠正我。 I'm trying to convert a small piece of C# streams code I wrote a while back for reading in a compressed stream.我正在尝试转换我不久前写的一小段 C# 流代码,用于读取压缩的 stream。

byte[] data = new byte[length - 1];
file.Read(data, 0, data.Length);

Stream ret = new ZlibStream(new MemoryStream(data), CompressionMode.Decompress, true);
return ret;

Data from part of a file is read into a memory buffer which feeds the zlib decompressor.来自文件的一部分的数据被读入 memory 缓冲区,该缓冲区为 zlib 解压缩器提供数据。 The consumer of the stream will pick away at it over time, and when it is finished will call Close() which combined with the garbage collector will clean up all of the resources. stream 的消费者将随着时间的推移将其捡走,当它完成时将调用Close()结合垃圾收集器将清理所有资源。 Note : an important distinction is I am not trying to decompress an entire file, just a small part of one.注意:一个重要的区别是我不是要解压整个文件,只是其中的一小部分。 The file has already been seeked to some interior location and length is small relative to the full size of the file.该文件已被搜索到某个内部位置,并且长度相对于文件的完整大小而言很小。

I'm trying to come up with the best equivalent to this in C++ code with Boost.我正在尝试在带有 Boost 的 C++ 代码中找到与此等效的最佳方法。 So far I have this moral equivalent to the above (untested):到目前为止,我的道德等同于上述(未经测试):

char * data = new char[length - 1];
_file.read(data, length - 1);

io::stream_buffer<io::basic_array_source<char> > buffer(data, length - 1);

io::filtering_stream<io::input> * in = new io::filtering_stream<io::input>;         
in->push(io::zlib_decompressor());
in->push(buffer);

return in;

I assume I could return the filtering_stream wrapped in a shared_ptr which would save the consumer from having to worry about deleting the stream, but I also have that data buffer new'd up in there.我假设我可以返回包装在shared_ptr中的过滤流,这将使消费者不必担心删除 stream,但我也有新的数据缓冲区。 Ideally I would like the consumer to just call close() on the stream and some mechanism (eg callback) would clean up the underlying resources in the filter.理想情况下,我希望消费者只在 stream 上调用close()并且某些机制(例如回调)会清理过滤器中的底层资源。 Requiring the consumer to pass the stream to an explicit release function is also acceptable, but I'm still not entirely sure how to get the underlying data buffer back in the first place.要求消费者将 stream 传递给显式发布 function 也是可以接受的,但我仍然不完全确定如何首先恢复底层数据缓冲区。

Cleaner alternative solutions are also welcome.更清洁的替代解决方案也受到欢迎。

Update 1更新 1

I've tried loosely seizing on the comment by Cat Plus Plus about a std::vector-backed driver.我尝试松散地抓住 Cat Plus Plus 关于 std::vector 支持的驱动程序的评论。 That's not quite what I've done, but this is what I have come up with so far.这不是我所做的,但这是我迄今为止想出的。 In the following code, I have a boost::shared_array-backed driver, based on the boost driver examples.在以下代码中,我有一个 boost::shared_array-backed 驱动程序,基于 boost 驱动程序示例。

namespace io = boost::iostreams;

class shared_array_source
{
public:

    typedef char            char_type;
    typedef io::source_tag  category;

    shared_array_source (boost::shared_array<char> s, std::streamsize n)
        : _data(s), _pos(0), _len(n)
    { }

    std::streamsize read (char * s, std::streamsize n)
    {
        std::streamsize amt = _len - _pos;
        std::streamsize result = (std::min)(amt, n);

        if (result != 0) {
            std::copy(_data.get() + _pos, _data.get() + _pos + result, s);
            return result;
        }
        else {
            return -1;
        }
    }

private:
    boost::shared_array<char> _data;
    std::streamsize _pos;
    std::streamsize _len;
};

Then I have my function that returns a stream然后我有我的 function 返回一个 stream

io::filtering_istream * GetInputStream (...)
{
    // ... manipulations on _file, etc.

    boost::shared_array<char> data(new char[length - 1]);
    _file.read(data.get(), length - 1);

    shared_array_source src(data, length - 1);
    io::stream<shared_array_source> buffer(src);

    io::filtering_istream * in = new io::filtering_istream;
    in->push(io::zlib_decompressor());
    in->push(buffer);

    // Exhibit A
    // uint32_t ui;
    // rstr->read((char *)&ui, 4);

    return in;
}

And in my main function of a test program:在我的主要 function 测试程序中:

int main () {
    boost::iostreams::filtering_istream * istr = GetInputStream();

    // Exhibit B
    uint32_t ui;
    rstr->read((char *)&ui, 4);

    return 0;
}

Ignore the fact that I'm returning a pointer that will never be freed -- I'm keeping this as simple as possible.忽略我返回的指针永远不会被释放的事实——我尽可能简单。 What happens when I run this?当我运行它时会发生什么? If I uncomment the code at Exhibit A, I get a proper readout in ui .如果我取消注释 Exhibit A 中的代码,我会在ui中得到正确的读数。 But when I hit Exhibit B, I crash deep, deep, deep down in Boost (sometimes).但是当我点击图表 B 时,我在 Boost 中深深地、深深地、深深地崩溃(有时)。 Well crap, I went out of scope and things broke, some local must be deconstructing and messing everything up.废话,我从 scope 出去了,东西坏了,一定是一些当地人在解构,把一切都搞砸了。 data is in a shared_array, in is on the heap, and the compressor is constructed according to docs. data在 shared_array 中, in在堆上,压缩器是根据文档构造的。

Are one of the boost constructors or functions grabbing a reference to an object on the stack (namely io::stream or filtering_stream's push)?升压构造函数或函数之一是否在堆栈上获取对 object 的引用(即 io::stream 或 filtering_stream 的推送)? If that's the case, I'm sort of back to square one with unmananaged objects on the heap.如果是这样的话,我有点回到堆上的非托管对象的问题上。

You really should avoid allocating anything on the heap.你真的应该避免在堆上分配任何东西。 filtering_stream can decompress on the fly, so you don't need to replace the buffer or read the file contents first. filtering_stream可以即时解压缩,因此您无需先替换缓冲区或读取文件内容。 The code should look more like this:代码应该看起来更像这样:

io::filtering_stream stream;
stream.push(io::zlib_decompressor());
stream.push(_file);

If you really need to allocate it on the heap, then yes, you should wrap it in a smart pointer (but again, don't read the file data first — you're risking the leak of that buffer, not to mention reading a large file this way can be terribly inefficient).如果你真的需要在堆上分配它,那么是的,你应该将它包装在一个智能指针中(但同样,不要先读取文件数据 - 你有该缓冲区泄漏的风险,更不用说读取一个这种方式的大文件可能非常低效)。

Ultimately I had to give up on trying to get Boost iostreams to clean up after themselves.最终,我不得不放弃尝试让 Boost iostreams 自行清理。 While creating a custom array device that internally used a boost::shared_array solved the problem of my data buffer being left on the heap, it turns out that boost::iostreams::filtering_stream / streambuf take a reference to an object pushed into the chain, which means it was capturing a reference to my device on the stack (as much as I could infer from the source and behavior, anyway).在创建内部使用 boost::shared_array 的自定义数组设备解决了我的数据缓冲区留在堆上的问题时,结果 boost::iostreams::filtering_stream / streambuf 引用了推入链中的 object ,这意味着它正在捕获对堆栈上我的设备的引用(无论如何我可以从源和行为中推断出)。 I could new up the device on the heap, but that just puts me back to square one.我可以在堆上更新设备,但这只会让我回到原点。

My following solution moves the creation of the stream into a thin std::istream wrapper, which can then be returned inside a boost::shared_ptr, allowing all resources to be cleaned up when the last reference is destroyed.我的以下解决方案将 stream 的创建移动到一个瘦 std::istream 包装器中,然后可以在 boost::shared_ptr 中返回它,从而允许在最后一个引用被销毁时清理所有资源。

template <class Compressor>
class CompressedIStream : public std::basic_istream<char, std::char_traits<char> >
{
public:
    CompressedIStream (std::istream& source, std::streamsize count)
        : _data(new char[count]), _buffer(_data, count), std::basic_istream<char, std::char_traits<char> >(&_filter)
    {
        source.read(_data, count);

        _filter.push(Compressor());
        _filter.push(_buffer);
    }

    virtual ~CompressedIStream ()
    {
        delete[] _data;
    }

private:
    char * _data;
    io::stream_buffer<io::basic_array_source<char> > _buffer;
    io::filtering_istreambuf _filter;
};

boost::shared_ptr<std::istream> GetInputStream (...)
{
    // ... manipulations on _file, etc.

    typedef CompressedIStream<io::zlib_decompressor> StreamType;
    return boost::shared_ptr<StreamType>(new StreamType(_file, length - 1));
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM