|
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