Boost logo

Boost :

From: Ferdinand Prantl (prantlf_at_[hidden])
Date: 2008-07-09 11:11:13


Hello,

I would like to wrap an input/output stream buffer (std::streambuf) with
boost::iostreams::code_converter to be able to use it both raw and with
ont-the-fly encoding by the boost::iostreams::default_facet.

    namespace io = boost::iostreams;
    xxxstream stream;
    stream << "raw ";
    io::stream<io::code_converter<xxxiodevice> > wrapper(*stream.rdbuf());
    wrapper << L"and converted";

I created a device wrapping std::streambuf. Pure input device (using
io::source_tag) and pure output device (using io::sink_tag) were working.
Combined device (using io::bidirectional_device_tag) failed with access
violation in destructor (flushing and closing) of the io::code_converter
in code_converter.hpp:

    template<typename Device, typename Codevt, typename Alloc>
    std::streamsize code_converter<Device, Codevt, Alloc>::write
        (const char_type* s, std::streamsize n)
    {
        buffer_type& buf = out();
        ...
            std::codecvt_base::result result = // buf.eptr() was null here
                cvt().out( buf.state(), s + total, s + n, nint,
                    buf.eptr(), buf.end(), next );
        ...
    }

I wondered how it is possible that the output buffer was not initialized.
I learnt that the buffer was intentionally skipped in code_converter.hpp:

    template<typename Device, typename Codecvt, typename Alloc>
    struct code_converter_impl {
        ...
        void open(const Device& dev, int buffer_size)
        {
            ...
            // following condition is false for bidirectional
            if (can_write::value && !is_double::value) {
                buf_.second().resize(buffer_size);
                buf_.second().set(0, 0);
            }
            ...
        }
        ...
    };

The reason is the definition of a bidirectional device in categories.hpp:

    struct bidirectional : virtual input, virtual output,
        detail::two_sequence {};

It is detected in the io::code_converter turning off the initialization:

    typedef is_convertible<device_category, two_sequence> is_double;

It means that I cannot use io::code_converter with io::bidirectional
combining both io::input and io::output tags. Unfortunately I was not able
to use these both explicitely:

        // original: typedef io::bidirectional_device_tag category;
        struct category : io::device_tag, io::input, io::output {};

The compiler complained about it with a message I was not able to fix:

    error C2784: 'std::basic_ostream<_Elem,_Traits> &boost::operator <<(
        std::basic_ostream<_Elem,_Traits> &,const boost::shared_ptr<Y> &)'
        : could not deduce template argument for 'std::basic_ostream<
            _Elem,_Traits> &' from 'boost::iostreams::stream<Device>'

So far, I found just one working solution without changing boost sources -
using two objects; one for reading, another one for writing:

    xxxstream stream;
    stream << "raw ";
    io::stream<io::code_converter<xxxidevice> > inwrapper(*stream.rdbuf());
    io::stream<io::code_converter<xxxodevice> > outwrapper(*stream.rdbuf());
    outwrapper << L"and converted";

However, I would like to use one object if it was possible. In my case,
all writing is always finished before reading and vice versa. Encoding states
in std::codecvt would not overlap. What would you suggest as a solution for
input/output devices in io::code_converter?

1. Stick with the two objects - one for input, one for output.
2. Fix io::code_converter to accept devices with io::bidirectional tag.
   Either initialize both buffers or reuse the input one.
3. Use different tag than io::bidirectional. Which one?
4. Maybe I am wrapping std::streambuf in a wrong way. Is there another?

I am attaching a simple example of a stripped source demonstrating the issue
on std::stringstream.

Thank you,
Ferda




Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk