
Hi, I started using the iostreams library for a pet project. I thinks it's a great piece of software. However, I immediately stumbled in what I think it may be a defect. Consider this simple source device: #include <boost/iostreams/stream.hpp> struct Source : boost::noncopyable { typedef char char_type; typedef boost::iostreams::source_tag category; Source(int); // ctor std::streamsize read(char* s, std::streamsize n) { // implementation here } private: /* ... private members here */ }; int main() { boost::iostreams::stream<Source> s(0); } the boost::noncopyable up there has been put because the device keeps handles to OS resources whose duplication can be very expensive. I was amazed that the code above doesn't compile. This is the error message reported by VC7.1: C:\lib\Boost\include\boost-1_33\boost\iostreams\detail\wrap_unwrap.hpp(52) : error C2558: struct 'Source' : no copy constructor available or copy constructor is declared 'explicit' (full error message is very long and is reported, for reference, at the end of the message) It seems that a device must be copy-constructibile. In particular, the code above makes at least three short-lived copies of the device. Question is: why? I believe this requirement is unintended and not due to a conscious design choice, my arguments are: 1) it's not documented, there is no mention of such requirement in http://www.boost.org/libs/iostreams/doc/concepts/device.html 2) it's unnatural and I believe my case (where copying the device could be expensive) occurs more often in practice than the opposite case 3) from the error messages it seems that a copy is only required as a side-effect of the template machinery used in BOOST_IOSTREAMS_FORWARD. In fact, the offending line detail\wrap_unwrap.hpp(52) is: inline T wrap(const T& t BOOST_IOSTREAMS_DISABLE_IF_STREAM(T)) { return t; } that requires T to be copy-constructible. Am I missing something? Thanks in advance, Ganesh ----- full error messaage follows C:\lib\Boost\include\boost-1_33\boost\iostreams\detail\wrap_unwrap.hpp(52) : error C2558: struct 'Source' : no copy constructor available or copy constructor is declared 'explicit' C:\lib\Boost\include\boost-1_33\boost\iostreams\stream_buffer.hpp(90) : see reference to function template instantiation 'T boost::iostreams::detail::wrap<T>(const T &,boost::disable_if_c<B,void>::type *)' being compiled with [ T=Source, B=false ] C:\lib\Boost\include\boost-1_33\boost\iostreams\stream_buffer.hpp(90) : while compiling class-template member function 'void boost::iostreams::stream_buffer<T,Tr,Alloc>::open(const T &,int,int)' with [ T=Source, Tr=std::char_traits<char>, Alloc=std::allocator<char> ] C:\lib\Boost\include\boost-1_33\boost\utility\base_from_member.hpp(69) : see reference to class template instantiation 'boost::iostreams::stream_buffer<T,Tr,Alloc>' being compiled with [ T=Source, Tr=std::char_traits<char>, Alloc=std::allocator<char> ] C:\lib\Boost\include\boost-1_33\boost\iostreams\stream.hpp(61) : see reference to class template instantiation 'boost::base_from_member<MemberType>' being compiled with [ MemberType=boost::iostreams::stream_buffer<Source,std::char_traits<char>,std::allocator<char>> ] C:\lib\Boost\include\boost-1_33\boost\iostreams\stream.hpp(98) : see reference to class template instantiation 'boost::iostreams::detail::stream_base<Device,Tr,Alloc>' being compiled with [ Device=Source, Tr=std::char_traits<char>, Alloc=std::allocator<char> ] TestVc2.cpp(26) : see reference to class template instantiation 'boost::iostreams::stream<Device>' being compiled with [ Device=Source ]

Alberto Ganesh Barbati wrote:
Hi,
I started using the iostreams library for a pet project. I thinks it's a great piece of software.
Thanks!
However, I immediately stumbled in what I think it may be a defect. Consider this simple source device:
struct Source : boost::noncopyable {
<snip>
};
int main() { boost::iostreams::stream<Source> s(0); }
the boost::noncopyable up there has been put because the device keeps handles to OS resources whose duplication can be very expensive.
This is a common situation. For example, the zlib filters have to manage non-copyable z_stream structures, and the file wrappers (e.g. file) manage non-copyable stream buffers from the standard library. There are two ways to handle this situation with the current library design: 1. Use reference counting. shared_ptr makes this easy; for examples see http://tinyurl.com/cu9gu or http://tinyurl.com/cu9gu. 2. Use boost::reference_wrapper. E.g., int main() { typedef boost::reference_wrapper<Source> source_ref; Source src(0); boost::iostreams::stream<source_ref> s(boost::ref(src)); }
I was amazed that the code above doesn't compile. This is the error message reported by VC7.1:
<snip>
It seems that a device must be copy-constructibile.
Actually, devices can be non-copyable, since streams and stream buffers are devices. I do mention this several places in the docs, for example, the docs for "Source" state that input streams are sources; still, it should be much more prominent. There are limitations on what can be done with non-copy-constructible devices. For example, you can't pass them to filtering_stream::push without using a reference wrapper, except in the common case of streams and stream buffers, which are given special treatment. The reason for requiring devices to be copy-constructible in most cases are described briefly here: http://tinyurl.com/7bptp. Exception safety was the main consideration: in most contexts, the alternative to passing copies would be to use dynamically allocated pointers, and this leads to problems for functions which take several filters or devices as arguments. It looks like I've never documented that the Device parameter to the class templates stream and stream_buffer must be copy-constructible; I'll fix this. In the example you give, which makes use of iostreams::stream's forwarding constructors, it would be theortically possible to construct the Source in-place, so that no copying is required. I guess it would be possible to have the copy-constructible requirement apply only to the constructors which take a const Source&. I must admit I've never thought of this. My guess is it will be difficult to make it work on all the supported compilers. Because of the to alternatives I mentioned above, I'm not sure it's worth the trouble. I'd be happy to apply a patch, if you submit one.
In particular, the code above makes at least three short-lived copies of the device. Question is: why? I believe this requirement is unintended and not due to a conscious design choice, my arguments are:
1) it's not documented, there is no mention of such requirement in http://www.boost.org/libs/iostreams/doc/concepts/device.html
Actually, it's documented extensively here: http://tinyurl.com/7bptp ;-)
Ganesh
-- Jonathan Turkanis www.kangaroologic.com

Jonathan Turkanis wrote:
Actually, it's documented extensively here:
That was supposed to be: http://www.boost.org/libs/iostreams/doc/index.html?path=3.7
;-)
Ganesh
-- Jonathan Turkanis www.kangaroologic.com

Jonathan Turkanis wrote:
That was supposed to be: http://www.boost.org/libs/iostreams/doc/index.html?path=3.7
Well... all I see is an empty page with the sentence "[To be supplied in the next release]", but I guess the page is simply not up-to-date ;-) Thanks for your explanation. As you said, it's a pity that we can't impose copy-constructibily only in the cases where it's really required, but the advantage is probably not worth the trouble in terms of effort and potential incompatibilies. In the end, I implemented my device wrapping the non-copiable part in a shared_ptr, it's not too inconvenient. In case you are wondering, I used boost.iostreams to provide a stream-like interface to pipes loosely inspired by Python's subprocess module <http://python.org/doc/2.4.2/lib/module-subprocess.html>. I am going to post a proposal in a separate thread. Ganesh

Alberto Ganesh Barbati wrote:
Jonathan Turkanis wrote:
That was supposed to be: http://www.boost.org/libs/iostreams/doc/index.html?path=3.7
Well... all I see is an empty page with the sentence "[To be supplied in the next release]", but I guess the page is simply not up-to-date ;-)
Hmmm ... I think I was trying to make a joke. The point is I never got around summarizing lifetime issues in one place, and just left a placeholder in the docs.
Thanks for your explanation. As you said, it's a pity that we can't impose copy-constructibily only in the cases where it's really required, but the advantage is probably not worth the trouble in terms of effort and potential incompatibilies. In the end, I implemented my device wrapping the non-copiable part in a shared_ptr, it's not too inconvenient.
Good.
In case you are wondering, I used boost.iostreams to provide a stream-like interface to pipes loosely inspired by Python's subprocess module <http://python.org/doc/2.4.2/lib/module-subprocess.html>. I am going to post a proposal in a separate thread.
Cool.
Ganesh
-- Jonathan Turkanis www.kangaroologic.com
participants (2)
-
Alberto Ganesh Barbati
-
Jonathan Turkanis