简体   繁体   English

boost :: iostreams :: copy()关闭源但不关闭接收器

[英]boost::iostreams::copy() closes the source but not the sink

I am trying to use boost::iostreams to compress data. 我试图使用boost :: iostreams来压缩数据。

The doc for copy() says that its two arguments are closed at the end by calling the template function close() on both of them. copy()的doc说它的两个参数最后通过在它们上面调用模板函数close()关闭 My test code is: 我的测试代码是:

#include <iostream>
#include <fstream>

#include <boost/iostreams/filtering_streambuf.hpp>
#include <boost/iostreams/copy.hpp>
#include <boost/iostreams/filter/gzip.hpp>

using namespace std;

int main(void)
{
    ifstream ifs("output", ios::binary);
    ofstream ofs("output.boost.gz", ios::binary);

    boost::iostreams::filtering_streambuf<boost::iostreams::output> out;

    out.push(boost::iostreams::gzip_compressor());
    out.push(ofs);

    cout << (ifs.is_open() ? "ifs is opened" : "ifs not opened") << endl;
    cout << (ofs.is_open() ? "ofs is opened" : "ofs not opened") << endl;

    boost::iostreams::copy(ifs, out);

    cout << (ifs.is_open() ? "ifs is opened" : "ifs not opened") << endl;
    cout << (ofs.is_open() ? "ofs is opened" : "ofs not opened") << endl;

    return 0;
}

This test outputs: 该测试输出:

ifs is opened
ofs is opened
ifs not opened
ofs is opened

You can see that ofs is still opened. 你可以看到仍然打开了。 My question is: why? 我的问题是:为什么? What does boost::iostreams::close() do when passed a filtering_streambuf object? boost :: iostreams :: close()在传递filtering_streambuf对象时做了什么?

Interesting. 有趣。

Going down the rabbit hole [1] it turns out that close_impl<any_tag> is finally reached for the ofstream wrapped deep inside the chain_buf inside the filtering_streambuf. 走下兔子洞[1] ,结果发现最终到达了close_impl<any_tag> ,因为ofstream包裹在filtering_streambuf里面的chain_buf深处。 The implementation reads: 实现如下:

template<>
struct close_impl<any_tag> {
    template<typename T>
    static void close(T& t, BOOST_IOS::openmode which)
    {
        if (which == BOOST_IOS::out)
            iostreams::flush(t);
    }

    template<typename T, typename Sink>
    static void close(T& t, Sink& snk, BOOST_IOS::openmode which)
    {
        if (which == BOOST_IOS::out) {
            non_blocking_adapter<Sink> nb(snk);
            iostreams::flush(t, nb);
        }
    }
};

So, as you can see, the documented behaviour is actually just that the linked output stream buffer(s) are flushed (there's also a synch on the containing entity prior to that call, IIRC). 因此,正如您所看到的,记录的行为实际上只是链接的输出流缓冲区被刷新(在该调用之前,包含的实体上也存在同步,IIRC)。

I completely agree that this could have been made a whole lot more explicit. 我完全同意这可以更加明确。

Reading the TMP code that decides on the specialization: 阅读决定专业化的TMP代码:

template<typename T>
struct close_tag {
    typedef typename category_of<T>::type             category;
    typedef typename detail::unwrapped_type<T>::type  unwrapped;
    typedef typename
            iostreams::select<
                mpl::not_< is_convertible<category, closable_tag> >,
                any_tag,
                mpl::or_<
                    is_boost_stream<unwrapped>,
                    is_boost_stream_buffer<unwrapped>
                >,
                close_boost_stream,
                mpl::or_<
                    is_filtering_stream<unwrapped>,
                    is_filtering_streambuf<unwrapped>
                >,
                close_filtering_stream,
                mpl::or_<
                    is_convertible<category, two_sequence>,
                    is_convertible<category, dual_use>
                >,
                two_sequence,
                else_,
                closable_tag
            >::type type;
};

Several workarounds come to mind: 我想到了几种解决方法:

  1. define a specialization of close_tag<> for std::ofstream that actually returns a different tag and make it so that it gets closed (I recommend against this since it can have unintended effects by going against the assumptions held by the devs of Boost Iostreams) std::ofstream定义一个close_tag<> ,它实际上返回一个不同的标签,然后使它关闭(我建议不要这样做,因为它可以通过违反Boost Iostreams开发者的假设而产生意想不到的效果)

  2. use a boost class for the output stream: 为输出流使用boost类:

#include <iostream>
#include <fstream>

#include <boost/iostreams/filtering_streambuf.hpp>
#include <boost/iostreams/copy.hpp>
#include <boost/iostreams/device/file.hpp>
#include <boost/iostreams/filter/gzip.hpp>

using namespace std;

int main(void)
{
    cout << boolalpha;

    ifstream ifs("output", ios::binary);
    boost::iostreams::file_sink ofile("output.boost.gz");

    boost::iostreams::filtering_streambuf<boost::iostreams::output> out;
    out.set_auto_close(true);

    out.push(boost::iostreams::gzip_compressor());
    out.push(ofile);

    cout << "out.is_complete(): " << out.is_complete() << endl;
    cout << "ifs.is_open()? "     << ifs.is_open()     << endl;
    cout << "ofile.is_open()? "   << ofile.is_open()   << endl;

    boost::iostreams::copy(ifs, out);

    cout << "out.is_complete(): " << out.is_complete() << endl;
    cout << "ifs.is_open()? "     << ifs.is_open()     << endl;
    cout << "ofile.is_open()? "   << ofile.is_open()   << endl;
}

See it Live on Coliru 在科利鲁看到它


[1] It is a surprisingly large rabbit hole, I must add. [1]我必须补充说,这是一个令人惊讶的大兔子洞。 I wonder what benefit all this genericity really has 我想知道这些通用性真正有什么好处

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

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