2013-12-20 1 views
3

Я пытаюсь использовать boost :: iostreams для сжатия данных.boost :: iostreams :: copy() закрывает источник, но не раковина

В документе doc for copy() указано, что его два аргумента закрыты в конце, вызывая функцию шаблона close() на обоих из них. Мой тестовый код:

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

Этот тест выходы:

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

Вы можете видеть, что OFS все еще открыт. Мой вопрос: почему? Что делает boost :: iostreams :: close(), когда передается объект filtering_streambuf?

ответ

4

Интересно.

Спускаясь кроличью нору [1] получается, что close_impl<any_tag> наконец достигнуто за ofstream завернутые глубоко внутри chain_buf внутри filtering_streambuf. Реализация гласит:

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

Итак, как вы можете видеть, документированы поведение фактически только что связанный выходной поток буфера (s) вымываются (есть также синхронизирующий на содержащую сущность до этого вызова , IIRC).

Я полностью согласен с тем, что это могло быть сделано гораздо более явным.

Чтение кода 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; 
}; 

несколько обходных путей приходят на ум:

  1. определяют специализацию close_tag<> для std::ofstream, что на самом деле возвращает другой тег и сделать его так что он закрывается (я рекомендую против этого, так как он может иметь непреднамеренные эффекты, исходя из предположений, сделанных разработчиками Boost Iostreams)

  2. использовать класс наддува для выходного потока:

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

Посмотреть Live on Coliru


[1] Это удивительно большой кролика отверстие , Я должен добавить. Интересно, какая польза от всей этой родословности действительно есть

+0

Отличный ответ, спасибо; Мне сложно понять обоснование этого поведения, а также какая часть документации документирует его. Считаете ли вы, что стоит попытаться связаться с разработчиками, чтобы получить ответ на это? – fireboot

+0

@fireboot думаю. Похоже, это было по дизайну. Документация относится к *** [документации «закрыть»] (http://www.boost.org/doc/libs/1_55_0/libs/iostreams/doc/functions/close.html#template_params_devices) ***, которая просто говорит «флеш» для чего-то не закрываемого – sehe

Смежные вопросы